举个例子:
1.用户登录了自己的银行页面 http://mybank.com,http://mybank.com向用户的cookie中添加用户标识。
2.用户浏览了恶意页面 http://evil.com。执行了页面中的恶意AJAX请求代码。
3.http://evil.com向http://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去。
4.银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。
5.而且由于Ajax在后台执行,用户无法感知这一过程。
以上就是所谓是CSRF(Cross-site request forgery)攻击,跨站请求伪造。接下来说一下 Django中处理csrf的方式
正常情况下直接发起一post请求,会报错。错误的意思是csrf校验失败,request请求被丢弃掉。
那么在django中的post失败有两种解决办法:
解决办法一:将csrf中间层注释掉
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
此时将不会进行csrf的校验,但如前面所述,这是一种不安全的行为。而且djano也不推荐使用
解决办法二:
在前面的提示中有这样一句话:
<form action="" method="post">{% csrf_token %}
也就是说在网页中加入csrf_token的标签就可以通过csrf校验
Django 提供的 CSRF 防护机制:
1、django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token,这样就能避免被 CSRF 攻击。
2、在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token,在所有的 POST 表单时,必须包含一个 csrfmiddlewaretoken 字段 (只需要在模板里加一个 tag, django 就会自动帮你生成,见下面)
3、在处理 POST 请求之前,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf 攻击,返回 403 Forbidden.
4、在所有 ajax POST 请求里,添加一个 X-CSRFTOKEN header,其值为 cookie 里的 csrftoken 的值
简介
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。
全局:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
注意:from django.views.decorators.csrf import csrf_exempt,csrf_protect
原理
当用post提交数据的时候,django会去检查是否有一个csrf的随机字符串,如果没有就会报错,这也是之前我们一直将其注释的原因,错误如下:
在django内部支持生成这个随机字符串
通过form提交
在form表单里面需要添加{%csrf_token%}
这样当你查看页面源码的时候,可以看到form中有一个input是隐藏的
总结原理:当用户访问login页面的时候,会生成一个csrf的随机字符串,,并且cookie中也存放了这个随机字符串,当用户再次提交数据的时候会带着这个随机字符串提交,如果没有这个随机字符串则无法提交成功
cookie中存放的csrftoken如下图
通过ajax提交
因为cookie中同样存在csrftoken,所以可以在js中通过:
$.cooke(“cstftoken”)获取
如果通过ajax进行提交数据,这里提交的csrftoken是通过请求头中存放,需要提交一个字典类型的数据,即这个时候需要一个key。
在views中的login函数中:from django.conf import settings,然后打印print(settings.CSRF_HEADER_NAME)
这里需要注意一个问题,这里导入的settings并不是我们在项目文件下看到的settings.py文件,这里是是一个全局的settings配置,而当我们在项目目录下的settings.py中配置的时候,我们添加的配置则会覆盖全局settings中的配置
print(settings.CSRF_HEADER_NAME)打印的内容为:HTTP_X_CSRFTOKEN
这里的HTTP_X_CSRFTOKEN是django在X_CSRF的前面添加了HTTP_,所以实际传递的是就是X_CSRFtoken,而在前端页面的ajax传递的时候由于不能使用下划线所以传递的是X_CSRFtoken
下面是在前端ajax中写的具体内容:
$("#btn1").click(function () {
$.ajax({
url:"/login/",
type:"POST",
data:{"usr":"root","pwd":"123"},
headers:{ "X-CSRFtoken":$.cookie("csrftoken")},
success:function (arg) {
}
})
})
但是如果页面中有多个ajax请求的话就在每个ajax中添加headers信息,所以可以通过下面方式在所有的ajax中都添加
$.ajaxSetup({
beforeSend:function (xhr,settings) {
xhr.setRequestHeader("X-CSRFtoken",$.cookie("csrftoken"))
}
});
这样就会在提交ajax之前执行这个方法,从而在所有的ajax里都加上这个csrftoken
这里的xhr是XMLHttpRequest的简写,ajax调用的就是这个方法
如果想要实现在当get方式的时候不需要提交csrftoken,当post的时候需要,实现这种效果的代码如下:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
这样就实现了当GET|HEAD|OPTIONS|TRACE这些方式请求的时候不需要提交csrftoken
总结
1、 csrf在ajax提交的时候通过请求头传递的给后台的
2、 csrf在前端的key为:X-CSRFtoken,到后端的时候django会自动添加HTTP_,并且最后为HTTP_X_CSRFtoken
3、 csrf在form中提交的时需要在前端form中添加{%csrftoken%}