Django跨站伪造请求保护措施设置方法[转]
转自:http://lanceverw.iteye.com/blog/1797885
在django建站中遇到post提交表单提示403错误, 发现以Post方式提交表单会触发django内置的csrf保护机制,并且在403页面给出了解决方法,根据提示更改后发现问题依旧,网上查阅很多同学的解决方案均不能解决这个问题,所以到官网上查阅了关于csrf部分的具体设置,最后成功解决了这个问题。
官方csrf部分设置翻译:
CSRF设置步骤:
第一步: 在settings.py中的MIDDLEWARE_CLASSES部分添加'django.middleware.csrf.CsrfViewMiddleware',如果使用普通方式创建的django项目,这一项一般都已经存在了不用额外设置。
如果你不希望加入csrf的中间件,那么可以在views中的方法上加上csrf_protect()装饰器方法,这一步不是必须设置的,网上很多有很多人都在启用了中间件的同时也加上了csrf_protect()方法,导致问题依旧,官方文档上明确写到了这一步是【Alternatively】
第二步:在POST表单的template中加入csrf_token。这一步很简单,在模板中添加如下代码:
- <form action="." method="post">{% csrf_token %}
需要注意的一点是,表单提交路径是如果是外部链接则不需要添加csrf token。
第三步:在请求处理方法中添加context processor。
这一步有两种方式实现:
方法1:使用RequestContext,RequestContext始终都使用了'django.core.context_processors.csrf'。If you are using generic views or contrib apps, you are covered already, since these apps use RequestContext throughout.(这一段还没搞明白,大致意思猜想是普通views或内置的contrib下的应用时,那么已经使用了这种方式来处理csrf请求了,不需要额外处理了)
方法2:手动添加csrf处理(我使用的这一种能成功访问)。示例代码如下:
- from django.core.context_processors import csrf
- from django.shortcuts import render_to_response
- def my_view(request):
- c = {}
- c.update(csrf(request))
- # ... view code here
- return render_to_response("a_template.html", c)
在第一次按照自己的设想将如上代码加入到view中时,发现问题依旧,后来将前两行代码放置在方法开始位置后,成功解决问题,由此推测该示例代码的前两行只能放在第一次使用request对象之前。
django中使用Ajax请求的CSRF设置:
将官方文档上的ajax设置照搬下来:
- 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 string 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 csrftoken = getCookie('csrftoken');
- function csrfSafeMethod(method) {
- // these HTTP methods do not require CSRF protection
- return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
- }
- $.ajaxSetup({
- crossDomain: false, // obviates need for sameOrigin test
- beforeSend: function(xhr, settings) {
- if (!csrfSafeMethod(settings.type)) {
- xhr.setRequestHeader("X-CSRFToken", csrftoken);
- }
- }
- });
这里使用的是jQuery,于是可以使用jQuery来正常发送ajax请求,如:
- $(document).ready(function(){
- $("#postBtn").click(function(){
- $.post('/ajax/aPost/', {id:'0001'}, function(data){
- alert(data);
- })
- });
- }
- );
get请求就不赘述了,同理,本文主要问题在于设置csrf说明,使用django版本为1.4.3,jQuery版本为1.8.0
注意:提到CsrfViewMiddleware的使用,很多人不是很了解整个流程。下面的代码,如果按照网上已有的教程是完全看不出错误在哪里的:
from django.shortcuts import render_to_response from django.core.context_processors import csrf def login_view(request): method = request.method.lower() c = {} c.update(csrf(request)) if method == 'get': return render_to_response('login.html', locals()) else method == 'post': return render_to_response('home.html', c)
断点调试,一直是一个好办法。我发现request是post的时候:
crsf.py里的代码部分执行过程,最终结果是找不到csrfmiddlewaretoken的值的,因为request.POST根本没有这个属性:
request_csrf_token = "" if request.method == "POST": request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
原因何在,在浏览器里查看,form表单下面没有type='hidden'的input存在,也就是说页面没有成功的渲染出能够提交csrfmiddlewaretoken的input。
仔细想一下,这个input必须是页面通过GET加载进来的时候生成的。也就是说get方式产生页面的时候,也要用RequestContext:
代码最终修改为:
from django.shortcuts import render_to_response from django.core.context_processors import csrf def login_view(request): method = request.method.lower() c = {} c.update(csrf(request)) if method == 'get': return render_to_response('login.html', c) else method == 'post': return render_to_response('home.html', c)
浏览器查看下页面:

再提交,成功跳转。
 
                    
                     
                    
                 
                    
                

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号