CSRF,中文名为跨站请求伪造,CSRF攻击指的是恶意用户盗用你的信息,以你的名义发送恶意请求。
这将包括:以你的名义发送邮件,发送信息,盗取账号,购买商品,甚至货币转账等恶性行为。
如图:
更具体的可以参阅这篇博客:
CSRF攻击与防御
这里不再过多介绍。
Django的CSRF中间件
和相关的模板标签提供了易于使用的csrf保护。
针对CSRF攻击的第一个保护措施是确保 safe
的请求方法(如GET、HEAD)没有副作用,对于需要提交数据的方法(如POST、PUT),可以按照如下步骤进行保护:
MIDDLEWARE
参数已经设置了django.middleware.csrf.CsrfViewMiddleware
中间件,如果你想要自定义这个设置,请确保该中间件位于已经假定CSRF攻击已经被处理过的视图中间件之前。【注】
不建议去禁用该中间件,但如果你禁用了它,你需要在你想要保护的视图中使用csrf_protect()以确保抵御CSRF攻击。
{% csrf_token %}
标签。如:<form method='post'>
{% csrf_token %}
...
</form>
但如果该POST表单以外部url为目标,则不应该这么做,因为这将导致csrf令牌泄露,从而产生漏洞。
RequestContext
作为上下文变量,RequestContext始终启用django.template.context_processors.csrf
,这样才能保证 {% csrf_token %} 正常工作。如果你使用的是render、类视图或者 contrib apps,那么不用担心,这些默认都是使用RequestContext的。虽然上面的方法可以用于Ajax POST请求,但是存在着不方便,因为每次Ajax,你都需要确保你是否将csrf_token作为POST数据一并传递了。因此这里还有一个替代方案,在每个 XMLHttpRequest 上设置一个自定义的 X-CSRFToken header
,该X-CSRFToken header
将作为csrf_token的值。该方案通常更加方便,因为许多Js框架都提供了允许在request上设置请求头的hooks
。
其中,X-CSRFToken header
可以由CSRF_HEADER_NAME
设置,CSRF_HEADER_NAME
是setting.py文件的一个配置项,它的默认值是HTTP_X_CSRFTOKEN
。该请求头与request.META中的其它请求头一样,通过将字符大写,连字符变下划线,以及添加HTTP_前缀,从而将从服务器中接收到的头名称标准化。
但是想要这么做,你仍然需要先获得csrf_token,具体怎样获得它将取决于CSRF_USE_SESSIONS
和CSRF_COOKIE_HTTPONLY
设置。
CSRF_USE_SESSIONS
:settings配置项。控制是否将csrf_token存储在用户session中而不是在cookie中,默认值是False。将csrf_token存储在cookie中是安全的(这也是Django的缺省值),但其它的Web框架常把它存储在session中。另外,默认的错误处理视图也需要csrf_token,因此如果使用CSRF_USE_SESSIONS,则应该在任何可能引发错误以导致错误视图的中间件之前插入SessionMiddleware
中间件。
CSRF_COOKIE_HTTPONLY
:settings配置项。默认值是False,控制是否在CSRF cookie上使用HTTPOnly
标志,如果该值设为True,则客户端的Js脚本将无法访问CSRF cookie。HttpOnly是一个包含在set-Cookie HTTP
响应头中的标志,它可以有效降低恶意攻击者的Js代码访问cookie的风险。与该配置项类似的还有SESSION_COOKIE_HTTPONLY
,它的默认值为True,功能类似,用于控制Js代码是否能获取到session cookie。
(1)当CSRF_USE_SESSIONS
和CSRF_COOKIE_HTTPONLY
均为False时获取csrf_token
这种情况下,csrf_token会保存在cookie,默认该cookie的名字是 csrftoken
cookie,你也可以通过CSRF_COOKIE_NAME
配置项将 csrftoken
改为其它的名字。
获取csrftoken cookie的代码将类似如下:
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 = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
通过现成的处理cookie的js库,还可以有更简单的方式获取它,一行代码就OK:
var csrftoken = Cookies.get('csrftoken');
该js库的github地址:js-cookie
【注意】
如果你在模板中显式使用了{% csrf_token %},那么在Dom中也会包含csrftoken,但是csrf中间件更优先使用cookie中的csrftoken,因此你也应该使用cookie。
如果你在模板中忘记了使用{% csrf_token %},那么Django可能将不会设置csrftoken cookie,在将表单动态的添加到页面中时,这种情况很常见,解决的办法是使用Django提供的 ensure_csrf_cookie()
视图装饰器,强制该视图渲染的模板必须携带 {% csrf_token %}。
(2)当CSRF_USE_SESSIONS
或CSRF_COOKIE_HTTPONLY
均为True时获取csrf_token
如果CSRF_USE_SESSIONS
为True,则csrf_token保存在session中,如果CSRF_COOKIE_HTTPONLY
为True,则尽管csrf_token保存在cookie中,但你无法提供Js代码获取到它。因此在这种情况下,我们必需要在Dom中获取csrf_token。
{% csrf_token %}
<script type="text/javascript">
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
</script>
最后,你还需要在Ajax请求头中设置token:
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);
}
}
});
与添加中间件的无差别保护不同,你可以在特定的视图中使用csrf_protect装饰器
,这些装饰器提供与csrf中间件相同的保护措施。它要求被修饰的视图必须能够接受POST表单数据,同时该视图的模板需要包含csrf_token标签。
用法:
from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def my_view(request):
c = {}
# ...
return render(request, "a_template.html", c)