关于Django的CSRF和跨域访问机制

The CSRF middleware and template tag provides easy-to-use protection against Cross Site Request Forgeries. This type of attack occurs when a malicious website contains a link, a form button or some JavaScript that is intended to perform some action on your website, using the credentials of a logged-in user who visits the malicious site in their browser. A related type of attack, ‘login CSRF’, where an attacking site tricks a user’s browser into logging into a site with someone else’s credentials, is also covered.
The first defense against CSRF attacks is to ensure that GET requests (and other ‘safe’ methods, as defined by RFC 7231#section-4.2.1) are side effect free. Requests via ‘unsafe’ methods, such as POST, PUT, and DELETE, can then be protected by following the steps below.

以上描述来自django官方文档种对CSRF中间件的描述,简单的来说,就是一个页面发出的请求必须来自于这个站点。拿银行来说,银行转账的url可能是这样www.xxbank.com/trans,POST参数为target_id,money;如果现在有一个钓鱼网站诱骗用户输入了银行账户和密码,然后在隐藏表单内提交target_idmoney那么钱就莫名其妙的被转走了。这也就是所谓的CSRF。

django为了解决这个问题,在POST,PUT,DELETE等可以修改数据的方法设置了csrf_token,当用户第一次访问网站的时候,服务端会产生一个随机的安全码作为token,并返回给客户端, 客户端会将token存在cookies里,在进行POST提交的时候,则必须带上这个token,以便于服务器验证。总的来说就是给客户端发令牌, 客户端带着令牌才可以执行操作,否则就返回403.

在前端的form表单里加上csrf_token,模板会默认在表单内增加一个隐藏项(命名为csrfmiddlewaretoken)。

1
2
3
4
5
6
<form action="/login">
{%csrf_token%}
<input type='text' name='username'/>
<input type='password' name='password'/>
<button type='submit'/>
</form>

对于ajax则需要手动去设置参数,在进行数据提交之前,在请求头内加上页面之前发送过来的token。django官方提供了从cookie里获得csrf-token的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//ajax设置csrf_token
function csrfSafeMethod(method) {
// these HTTP methowds do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie s begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrf_token = getCookie('csrf_token');
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrf_token);
}
}
});

执行上述操作之后,在进行正常的ajax提交即可。

有时候部分浏览器可能拿不到token,这个时候需要使用到装饰器ensure_csrf_cookie.用来装饰需要使用token的view,确保token在第一次访问时被传递到客户端。

同时,还可以使用 csrf_protecte或csrf_exempt 强制要求一个view使用或不使用CSRF中间件。

默认情况下,token会被放在cookies里,但也可放在session内,可通过settings配置文件修改,但匿名用户是没有session的。

文章目录