问题描述
CSRF与XSS:
CSRF(跨域请求伪造):黑客网站盗用用户在A网站的cookie向A网站发送请求

如何防御:
Token验证(使用最多):服务器发送一个Token给客户端,客户端提交的表单请求要携带这个Token,如果Token不合法服务器拒绝这个请求
隐藏令牌:把Token隐藏在http的head头中,本质上与方法一没有太大区别
Referer验证:Referer指的是页面请求来源。意思是只接受本站的请求,服务器才做响应
XSS(跨域脚本攻击):不需要你做任何登录认证,通过合法的操作(如在url输入、在评论框输入),向你的页面注入脚本(js、html)。主要分反射型和存储型攻击
如何防御:
编码:对用户输入的数据进行HTML Entity编码,将脚本转换为纯文本,对特殊符号转义处理
过滤:移除用户输入数据的Script节点、iframe节点
同源策略:同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓跨域访问指前端发起网络请求时与后端不在同一个ip下或者不在同一个端口下,如:
- 127.0.0.1:8080 到 127.0.0.1:8000
127.0.0.1 到 127.0.0.2
都属于跨域访问。
同源策略限制内容有:
- Cookie、LocalStorage、IndexedDB等存储性内容
- DOM节点
- AJAX请求
跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
但是有三个标签是允许跨域加载资源的:
- img
- link
- script
项目环境
- Vue
- Nginx
- Django
跨域解决方案
jsonp
原理:利用script标签没有跨域限制的漏洞,网页可以得到其他来源动态产生的json数据。但是仅支持get方法具有局限性,不安全可能会遭受XSS攻击
实现流程:创建一个script标签,并设置一个回调函数,类似于前端设置好一个函数,后端返回一个执行该函数的命令
1// 前端代码2function jsonp(url){3var frame = document.createElement('script');4frame.src = url;5$('body').append(frame);6});78// 预先设置好的回调函数9function func(res){10alert(res.message+res.name+'你已经'+res.age+'岁了');11}1// 服务端代码2router.get('/article-list', (req, res) => {3console.log(req.query, '123');4let data = {5message: 'success!',6name: req.query.name,7age: req.query.age8}9data = JSON.stringify(data)10res.end('func(' + data + ')');11});
CORS
原理:服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。
option请求:
在正式的请求前,浏览器会根据需要,发起一个“PreFlight”(也就是option请求),来让服务器返回允许的方法(如get、post),被跨域访问的Origin,或者是否需要Credentials(认证信息)
如果是简单请求则不会触发PreFlight,浏览器对简单请求的要求是:
- 只能是GET、HEAD、POST方法
- 除了浏览器自己在http头上加的信息(如Connection、User-Agent),开发者只能加:Accept、Accept-Language、Content-Type
- ContentType只能取这几个值:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- 服务器可以设置Access-Control-Max-Age来控制浏览器在多长时间内无需再发送预检请求,从而减少不必要的预检请求
解决方案
安装django-cors-headers,
pip install django-cors-headers修改
setting.py1INSTALLED_APPS = [2...3'corsheaders',4...5]67# 添加中间件8MIDDLEWARE = [9'django.middleware.security.SecurityMiddleware',# 默认10'django.contrib.sessions.middleware.SessionMiddleware', # 默认1112'corsheaders.middleware.CorsMiddleware',# 新增 ✔13# 注意顺序14'django.middleware.common.CommonMiddleware',1516'django.middleware.csrf.CsrfViewMiddleware',# 默认17'django.contrib.auth.middleware.AuthenticationMiddleware',# 默认18'django.contrib.messages.middleware.MessageMiddleware', # 默认19'django.middleware.clickjacking.XFrameOptionsMiddleware',# 默认20'django.middleware.common.CommonMiddleware',# 默认21]2223# 跨域增加忽略24CORS_ALLOW_CREDENTIALS = True25CORS_ORIGIN_ALLOW_ALL = True26# 如果报错请注释这一句27CORS_ORIGIN_WHITELIST = (28'*'29)30CORS_ALLOW_METHODS = (31'DELETE',32'GET',33'OPTIONS',34'PATCH',35'POST',36'PUT',37'VIEW',38)39CORS_ALLOW_HEADERS = (40'XMLHttpRequest',41'X_FILENAME',42'accept-encoding',43'authorization',44'content-type',45'dnt',46'origin',47'user-agent',48'x-csrftoken',49'x-requested-with',50)51#部署到云服务上必备52ALLOWED_HOSTS = ['*']如果需要携带cookie跨域,还需要在设置一下axios
axios.defaults.withCredentials = true,切记cookie的domain要和发送请求的origin一致,不然会出现发送请求没有携带cookie的问题(注:localhost与127.0.0.1不等价)
postMessage
原理:postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的iframe消息传递
- 上面三个场景的跨域数据传递
实现方法
1// a.html2<iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe> //等它加载完触发一个事件3//内嵌在http://localhost:3000/a.html4<script>5function load() {6let frame = document.getElementById('frame')7frame.contentWindow.postMessage('我爱你', 'http://localhost:4000') //发送数据8window.onmessage = function(e) { //接受返回数据9console.log(e.data) //我不爱你10}11}12</script>1// b.html2window.onmessage = function(e) {3console.log(e.data) //我爱你4e.source.postMessage('我不爱你', e.origin)5}
使用代理服务器
nginx
修改nginx配置文件nginx.conf
1server {2listen 81;3server_name www.domain1.com;4location / {5proxy_pass http://www.domain2.com:8080; #反向代理6proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名7index index.html index.htm;89# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用10add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*11add_header Access-Control-Allow-Credentials true;12}13}