问题描述
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
// 前端代码
2
function jsonp(url){
3
var frame = document.createElement('script');
4
frame.src = url;
5
$('body').append(frame);
6
});
7
8
// 预先设置好的回调函数
9
function func(res){
10
alert(res.message+res.name+'你已经'+res.age+'岁了');
11
}
1
// 服务端代码
2
router.get('/article-list', (req, res) => {
3
console.log(req.query, '123');
4
let data = {
5
message: 'success!',
6
name: req.query.name,
7
age: req.query.age
8
}
9
data = JSON.stringify(data)
10
res.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.py
1
INSTALLED_APPS = [
2
...
3
'corsheaders',
4
...
5
]
6
7
# 添加中间件
8
MIDDLEWARE = [
9
'django.middleware.security.SecurityMiddleware',# 默认
10
'django.contrib.sessions.middleware.SessionMiddleware', # 默认
11
12
'corsheaders.middleware.CorsMiddleware',# 新增 ✔
13
# 注意顺序
14
'django.middleware.common.CommonMiddleware',
15
16
'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
]
22
23
# 跨域增加忽略
24
CORS_ALLOW_CREDENTIALS = True
25
CORS_ORIGIN_ALLOW_ALL = True
26
# 如果报错请注释这一句
27
CORS_ORIGIN_WHITELIST = (
28
'*'
29
)
30
CORS_ALLOW_METHODS = (
31
'DELETE',
32
'GET',
33
'OPTIONS',
34
'PATCH',
35
'POST',
36
'PUT',
37
'VIEW',
38
)
39
CORS_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
#部署到云服务上必备
52
ALLOWED_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.html
2
<iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe> //等它加载完触发一个事件
3
//内嵌在http://localhost:3000/a.html
4
<script>
5
function load() {
6
let frame = document.getElementById('frame')
7
frame.contentWindow.postMessage('我爱你', 'http://localhost:4000') //发送数据
8
window.onmessage = function(e) { //接受返回数据
9
console.log(e.data) //我不爱你
10
}
11
}
12
</script>
1
// b.html
2
window.onmessage = function(e) {
3
console.log(e.data) //我爱你
4
e.source.postMessage('我不爱你', e.origin)
5
}
使用代理服务器
nginx
修改nginx配置文件nginx.conf
1
server {
2
listen 81;
3
server_name www.domain1.com;
4
location / {
5
proxy_pass http://www.domain2.com:8080; #反向代理
6
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
7
index index.html index.htm;
8
9
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
10
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
11
add_header Access-Control-Allow-Credentials true;
12
}
13
}