一、CSRF攻击(Cross Site Request Forgery,跨站请求伪造):
是攻击者通过跨站请求,以合法的用户身份进行非法操作(如转账或发帖等)。CSRF的原理是利用浏览器的Cookie或服务器的Session,盗取用户身份,其原理如下图所示。
防范CSRF的主要手段是识别请求者的身份,主要有以下几种方式:
- 在表单中添加令牌(token)
- 验证码
- 检查请求头中的Referer(前面提到防图片盗链接也是用的这种方式)
令牌和验证都具有一次消费性的特征,因此在原理上一致的,但是验证码是一种糟糕的用户体验,不是必要的情况下不要轻易使用验证码,目前很多网站的做法是如果在短时间内多次提交一个表单未获得成功后才要求提供验证码,这样会获得较好的用户体验。
二、Django CSRF 中间件:
1.概述:
为了避免上面情况的出现,Django引用了CSRF防护机制;Django为了防止跨站请求伪造,即csrf攻击,提供了CsrfViewMiddleware中间件来防御csrf攻击。Django第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,并把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token,这样就能避免被 CSRF 攻击。如果POST请求中没有token随机字符串,则返回403拒绝服务。
- 在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token
- 在所有的 POST 表单时,必须包含一个 csrfmiddlewaretoken 字段 (只需要在模板里加一个 tag, django 就会自动帮你生成,见下面)
- 在处理 POST 请求之前,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf 攻击,返回 403 Forbidden。
- 在所有 ajax POST 请求里,添加一个 X-CSRFTOKEN header,其值为 cookie 里的 csrftoken 的值
2.启用方式:
(1)settings里面全局启用:
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'middleware.authenticate.authenticateMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', # csrf
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'middleware.logStash.logStashMiddleware',
]
(2)局部使用方法:
先导入
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
三、解决方案:
阅读django CsrfViewMiddleware源码可知,csrftoken可以放在请求参数(csrfmiddlewaretoken)里面或者请求头(X-CSRFToken)里:
axios.defaults.withCredentials = false;
axios.interceptors.request.use((config) => {
config.headers['X-Requested-With'] = 'XMLHttpRequest';
config.headers['X-CSRFToken'] = cookie.parse(document.cookie).csrftoken;
return config
});
将csrf令牌写入Cookie,是因为:
服务器进行csrf防御校验的时候,是拿用户http请求体中的token参数值和cookie中的csrftoken值进行比对。如果值一样了,操作才被允许执行。所以将CSRF-TOKEN写在COOKIE中符合就CSRF防御思想中的不可预知原则。为什么CSRF Token写在COOKIE里面我们这里设置axios.defaults.withCredentials = false;在跨域请求时,不会携带用户凭证;而是我们把从cookie取来的的csrftoken自定义放到请求头里面
添加后的效果:
四、拓展:
axios.defaults.headers.post['Content-Type'] = 'application/x-www-fromurlencodeed';
axios.defaults.withCredentials = false;
axios.interceptors.request.use((config) => {
config.headers['X-Requested-With'] = 'XMLHttpRequest';
config.headers['X-CSRFToken'] = cookie.parse(document.cookie).csrftoken;
return config
});
这是使用vue接口请求我们需要添加的一些配置,下面做个介绍:
- List itemaxios的post请求,默认是application/json提交JSON格式的数据,实际我们后端要求的 ‘Content-Type’: ‘application/x-www-form-urlencoded’ ,这时需要配置content-type:
axios.defaults.headers.post[‘Content-Type’] = ‘application/x-www-fromurlencodeed’;
- 如果 requestedWith 为 null,则为同步请求。如果 requestedWith 为 XMLHttpRequest 则为 Ajax 请求(异步)。
config.headers[‘X-Requested-With’] = ‘XMLHttpRequest’;
- withCredentials:默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。默认值为false;true:在跨域请求****时,会携带用户凭证;false:在跨域请求时,不会携带用户凭证;返回的 response 里也会忽略 cookie,当配置了 withCredentials = true时,必须在后端增加 response 头信息Access-Control-Allow-Origin,且必须指定域名,而不能指定为*!!!
axios.defaults.withCredentials = false;