Django-CSRF跨站请求伪造
跨站请求伪造
CSRF的全称是`Cross-site request forgery`,简单的意思就是用户在可信网站登录后获得可信cookie,未退出之前,访问了一个恶意网站,恶意网站夹带攻击性代码要求访问一个第三方网站,浏览器在接收到恶意网站的请求后,在用户不知情的情况下携带安全网站给的Cookie信息,向其发出请求。可信网站并不知道该请求其实是由恶意网站发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自恶意网站的恶意代码被执行
Django是如何防范CSRF的
我们来看看CsrfViewMiddleware
这个中间件的源码的一段内容
...
request_csrf_token = ""
if request.method == "POST":
try:
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') # POST请求是在这里获取到的
except IOError:
# Handle a broken connection before we've completed reading
# the POST data. process_view shouldn't raise any
# exceptions, so we'll ignore and serve the user a 403
# (assuming they're still listening, which they probably
# aren't because of the error).
pass
if request_csrf_token == "":
# Fall back to X-CSRFToken, to make things easier for AJAX,
# and possible for PUT/DELETE.
request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') # ajax请求是在获取的
request_csrf_token = _sanitize_token(request_csrf_token)
if not _compare_salted_tokens(request_csrf_token, csrf_token):
return self._reject(request, REASON_BAD_TOKEN)
...
当我们手动输出settings.CSRF_HEADER_NAME
的结果是这样的:
from django.conf import settings
print(settings.CSRF_HEADER_NAME)
>>> HTTP_X_CSRFTOKEN
Django使用django.middleware.csrf.CsrfViewMiddleware
这一个中间件来完成验证的。在django中防御csrf攻击的方式有两种:
1. 在form表单中添加csrf_token
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
</style>
</head>
<body>
<div>
<form action="/login/" method="post">
{% csrf_token %}
<input type="text" name="user">
<input type="text" name="pwd">
<input type="submit" value="提交">
</form>
</div>
<script>
</script>
</body>
</html>
在form中加上这个{% csrf_token %}
后其实他的本质是生成一个隐藏的input标签,并且input的value值是服务器端给网页get的时候生成的一个随机字符串,如下所示:
![](https://img2020.cnblogs.com/blog/1119052/202003/1119052-20200319130508084-1070406822.png)
在form提交的时候顺带将这个值发送给服务器做验证
2. 在header中添加X-CSRFToken
- 当我们使用一般的ajax提交的时候
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
$(function () {
$('#btn').click(function () {
$.ajax({
url:'/login/',
type:'POST',
data:$('#f1').serialize(),
success:function () {
}
})
})
})
</script>
这样写ajax也会出现403CSRF verification failed. Request aborted.
那么我们的ajax就应该在header中加上CSRF_HEADER_NAME
对应的值HTTP_X_CSRFTOKEN
,但是由于django会在header的字段的首部自动加上HTTP_
来作为区分,所以我们应该添加的真正值是X_CSRFTOKEN
,但是又由于请求头中不能加下划线,所以只能写成这样了X-CSRFTOKEN
,官方的书写方式是X-CSRFtoken
,但是这两种都是可以的。
通过js来获取csrftoken
这个cookie并添加到header中
正确的ajax请求应该是这样的
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
$(function () {
$('#btn').click(function () {
$.ajax({
url:'/login/',
type:'POST',
headers:{"X-CSRFtoken": $.cookie('csrftoken'),}
data:$('#f1').serialize(),
success:function (arg) {
location.href="/index/"
}
})
})
})
</script>
3.将页面中所有的ajax请求都添加这个headers
$.ajaxSetup({
beforeSend: function(xhr,settings){
xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken'));
}
});
django中选择使用CSRF
部分设定:from django.views.decorators.csrf import csrf_exempt,csrf_protect
- @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件
- @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
全局设定:
使用中间件django.middleware.csrf.CsrfViewMiddleware