Могу ли я заставить Uploadify работать с Django ›=1.2.5, используя параметр script-data для прохождения CSRF-валидации?

Из-за изменения политики CSRF для запросов AJAX, начиная с Django 1.2.5, все реализации Uploadify кажутся неработоспособными. Есть несколько попыток и некоторые довольно размытые ответы, но решения пока нет. Единственный обходной путь прямо сейчас, кажется, использует декоратор @csrf_exempt, как указано в этом сообщении: uploadify">Исправление ошибки django csrf при использовании uploadify

Несмотря на то, что Пол Макмиллан указал на причину этой проблемы, он не предложил ее решения (кроме изучения ActionScript и переписывания Uploadify). Для тех, кто использует Django и jQuery, было бы интересно получить более конкретную информацию по этой теме, так как не у всех есть время для изучения ActionScript. Мне особенно любопытно, может ли быть решение, использующее параметр данных сценария 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, действительно появляются в запросе.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:

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 полагается на файл cookie CSRF, а uploadify не передает файл cookie. Но он может передавать значения csrfmiddlewaretoken и session_id через scriptData. Разве это не сохранит защиту CSRF, говоря Django не искать csrf-cookie, а соответствующие значения внутри request.POST?

Что я хотел сказать по сути: устанавливайте "biscuit" не вслепую, а после проверки важных значений (csrfmiddlewaretoken, sessionid, что еще?). Я думаю, что это может сработать, хотя я не уверен, что полностью понял механизм защиты csrf...


person marue    schedule 21.04.2011    source источник
comment
Я хотел бы найти хорошее решение для этого! Просто потратьте 5 часов, пытаясь что-то сделать, и ничего не сработало.   -  person miki725    schedule 19.07.2011


Ответы (2)


У меня была такая же проблема, как у вас.

Начиная с Django 1.2.5, Django проверяет CSRF на ВСЕ запросы к нему. Причина в том, что ребята из Google нашли способ подделать запрос на любой URL-адрес с настраиваемым заголовком. Итак, теперь единственный способ проверить Django CSRF — это либо иметь файл cookie CSRF_token, либо отправить заголовок X-CSRFToken, который будет иметь значение токена CSRF. Примечания к выпуску по этому поводу можно найти здесь.

Насколько я понимаю, это невозможно исправить в Uploadify, потому что Uploadify использует SWFObject для фактической отправки данных, то есть Flash, а Flash не позволяет добавлять пользовательские заголовки.

Однако этот загрузчик работает исключительно с использованием объекта XHR для отправки данных или использует iFrame для других целей. - поддержка браузеров (у меня не было изменений, чтобы проверить решение, хотя, когда оно возвращается к iFrame, оно отлично работает при использовании объекта XHR). Кроме того, он основан на jQuery. Здесь представлена ​​демонстрационная реализация того, как этот загрузчик реализован в Django, однако он по-прежнему свободен от CSRF. .

Включить проверку CSRF для этой демонстрации можно, добавив фрагмент JS для jQuery из документации Django (здесь). Что это вырезало, так это то, что оно перезаписывает поведение AJAX по умолчанию в jQuery, и при каждом запросе 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'));
    }
});

Надеюсь это поможет.

person miki725    schedule 21.07.2011
comment
извините, что заставил вас ждать так долго. ответ действительно хорош, спасибо. - person marue; 27.12.2011

Поздний ответ, но в дополнение к вышесказанному, если вы используете csrftoken cookie, используйте декоратор, чтобы убедиться, что он установлен.

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def home(request):
     ....
person jackotonye    schedule 09.10.2017