Могу ли я заставить Uploadify работать с Django> = 1.2.5, используя опцию script-data для прохождения CSRF-проверки? - PullRequest
1 голос
/ 21 апреля 2011

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

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

Middleware:

#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?

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

Ответы [ 2 ]

0 голосов
/ 09 октября 2017

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

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def home(request):
     ....
0 голосов
/ 21 июля 2011

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

Начиная с Django 1.2.5, Django проверяет CSRF на ВСЕХ запросах к нему.Причина в том, что люди Google нашли способ подделать запрос к любому URL с пользовательским заголовком.Так что теперь единственный способ проверить CSRF Django - это получить файл 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 docs):

$(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'));
    }
});

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

...