我可以使用script-data选项通过CSRF验证将Uploadify与Django> = 1.2.5配合使用吗?

时间:2011-04-21 18:40:00

标签: jquery django uploadify

由于自Django 1.2.5以来AJAX请求的CSRF策略发生了变化,所以Uploadify的所有实现似乎都被打破了。有一些尝试和一些相当模糊的答案,但还没有解决方案。现在唯一的解决方法似乎是使用@csrf_exempt装饰器,如本文所述:Fixing django csrf error when using uploadify

尽管Paul McMillan指出了这个问题的原因,但他没有提出解决方案(除了学习Actionscript和重写Uploadify)。对于任何使用Django和jQuery的人来说,在这个主题上更具体一点会很有趣,因为不是每个人都有时间学习动作脚本。我特别好奇是否可以使用Uploadify的脚本数据选项解决方案,我无法开始工作。

$('#fileInput').uploadify({
'uploader'  : '{{ uploadify_path }}uploadify.swf',
'script'    : '{% url uploadify_upload %}',

//this is the interesting line
'scriptData': {"CSRF-Token" : $('input[name="csrfmiddlewaretoken"]').val()}
                },
'cancelImg' : '{{ uploadify_path }}cancel.png',
'auto'      : false,
'folder'    : '{{ upload_path }}',
'multi'     : true,
'onAllComplete' : allComplete
});

我认为这可行,script-data选项中指定的数据确实出现在request.POST dict中。我用pdb检查并查找请求:

@csrf_exempt
def upload(request, *args, **kwargs):
    if request.method == 'POST':
        if request.FILES:
            upload_received.send(sender='uploadify', data=request.FILES['Filedata'])
    import pdb; pdb.set_trace();
    return HttpResponse(request)  

这就是结果:

<WSGIRequest
GET:<QueryDict: {}>,
POST:<QueryDict: {u'CSRF-Token': [u'de885c962f9a2e50fec140e161ca993e'], u'folder': [u'/static/uploads/'], u'Upload': [u'Submit Query'], u'Filename': [u'P4010040.JPG']}>,
COOKIES:{},
META:{'App 

and so on, the rest as expected  

这几乎与上述帖子的回答中提出的解决方案相同,但该解决方案将破坏CSRF保护。我可以以某种方式使用scriptData来传递CSRF验证,而不会破坏保护吗?我需要哪些信息才能通过验证,我该如何使用它?

修改
我提到的帖子使用了这个解决方案,打破了csrf-protection:

使用Javascript:

biscuit = document.cookie;
csrt = $('input[name="csrfmiddlewaretoken"]').val();
$('#file_upload').uploadify({
      // pass the cookie and the csrftoken
      scriptData : {biscuit: biscuit, csrfmiddlewaretoken: csrf},
      .... // other codes
 });

中间件:

#insert after: 'django.middleware.common.CommonMiddleware'
def process_request(self, request):
    if (request.method == 'POST'):
       if request.POST.has_key('biscuit'):
          biscuit = request.POST['biscuit']
          tmp = map(lambda x: tuple(x.split("=")), biscuit.split(" "))
          # set a cookie
          request.COOKIES.update(tmp)

如果直接检查csrfmiddlewaretoken和session_id的正确值会怎么样?主要问题是Djangos CSRF保护依赖于CSRF cookie,而uploadify不会传递cookie。但它可以通过scriptData传递csrfmiddlewaretoken和session_id的值。它不会保留CSRF保护,告诉Django不要查找csrf-cookie,而是查找request.POST中的相关值吗?

我本来想说的是:不要盲目地设置“饼干”,但是在检查了重要值后(csrfmiddlewaretoken,sessionid,还有什么?)。我认为这可行,尽管我不确定我是否完全理解csrf保护的机制......

2 个答案:

答案 0 :(得分:0)

我遇到了和你一样的问题。

从Django 1.2.5开始,Django会检查所有请求的CSRF。原因是因为Google人员找到了如何使用自定义标头向任何URL伪造请求的方法。所以现在验证Django CSRF的唯一方法是使用CSRF_token cookie,或者发送一个具有CSRF令牌值的X-CSRFToken头。有关此问题的发行说明可以在here找到。

根据我的理解到目前为止,无法在Uploadify中修复它,因为Uploadify使用SWFObject实际发送数据,这是Flash和Flash不允许添加自定义标头。

然而,

This上传程序纯粹通过使用XHR对象发送数据或者回退到iFrame用于非支持的浏览器(我没有更改以检查解决方案,但是当它回退到iFrame但是它可以工作完美地使用XHR对象时)。另外它是基于jQuery的。 Here是如何在Django中实现此上传器的演示实现,但它仍然是CSRF免除。

为此演示启用CSRF验证的方法是从Django文档(here)添加JS snipped for jQuery。它剪切了它在jQuery中覆盖默认的AJAX行为并在每个AJAX请求上添加自定义头(X-CSRFToken)和CSRF令牌的值。现在由于上传器是基于jQuery的,因此它所做的所有AJAX请求都将是CSRF有效的。

这是剪辑(再次来自Django文档):

$(document).ajaxSend(function(event, xhr, settings) {
    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;
    }
    function sameOrigin(url) {
        // url could be relative or scheme relative or absolute
        var host = document.location.host; // host + port
        var protocol = document.location.protocol;
        var sr_origin = '//' + host;
        var origin = protocol + sr_origin;
        // Allow absolute or scheme relative URLs to same origin
        return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
            (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
            // or any other URL that isn't scheme relative or absolute i.e relative.
            !(/^(\/\/|http:|https:).*/.test(url));
    }
    function safeMethod(method) {
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

    if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    }
});

希望这有帮助。

答案 1 :(得分:0)

延迟回复但除了上述内容之外,如果使用csrftoken cookie,请使用装饰器来确保设置。

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def home(request):
     ....