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。这一步很简单,在模板中添加如下代码:

Html代码  收藏代码
  1. <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处理(我使用的这一种能成功访问)。示例代码如下:

Python代码  收藏代码
  1. from django.core.context_processors import csrf  
  2. from django.shortcuts import render_to_response  
  3.   
  4. def my_view(request):  
  5.     c = {}  
  6.     c.update(csrf(request))  
  7.     # ... view code here  
  8.     return render_to_response("a_template.html", c)  

    在第一次按照自己的设想将如上代码加入到view中时,发现问题依旧,后来将前两行代码放置在方法开始位置后,成功解决问题,由此推测该示例代码的前两行只能放在第一次使用request对象之前。

 

django中使用Ajax请求的CSRF设置:

将官方文档上的ajax设置照搬下来:

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

 这里使用的是jQuery,于是可以使用jQuery来正常发送ajax请求,如:

Js代码  收藏代码
  1. $(document).ready(function(){  
  2.         $("#postBtn").click(function(){  
  3.             $.post('/ajax/aPost/', {id:'0001'}, function(data){  
  4.                  alert(data);  
  5.             })  
  6.         });  
  7.     }  
  8. );  

 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)

 

浏览器查看下页面:

再提交,成功跳转。

posted @ 2013-07-22 19:20  小侠女  阅读(354)  评论(0)    收藏  举报