Ошибка проверки Django CSRF с запросом AJAX POST

я мог бы использовать некоторую помощь в соответствии с механизмом защиты CSRF Django через мой пост AJAX. Я следовал указаниям здесь:

http://docs.djangoproject.com/en/dev/ref/contrib/csrf/

Я скопировал пример кода AJAX, который у них есть на этой странице:

http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax

я поставил предупреждение печать содержимого getCookie('csrftoken') до xhr.setRequestHeader звонка и его действительно заполнен некоторыми данными. Я не уверен, как проверить правильность токена, но меня обнадеживает, что он находит и отправляет что-то.

но Джанго все еще отвергает мой пост AJAX.

вот мой JavaScript:

$.post("/memorize/", data, function (result) {
    if (result != "failure") {
        get_random_card();
    }
    else {
        alert("Failed to save card data.");
    }
});

вот ошибка, которую я вижу от Django:

[23 / Feb / 2011 22:08: 29] "POST /memorize/ HTTP/1.1" 403 2332

Я уверен, что чего-то не хватает, и, может быть, это просто, но я не знаю, что это такое. Я искал вокруг так и увидел некоторую информацию о выключении проверки CSRF для моего представления через csrf_exempt декоратор, но я нахожу это непривлекательным. Я пробовал это, и это работает, но я бы предпочел, чтобы мой пост работал так, как Django был разработан, чтобы ожидать этого, если это возможно.

на всякий случай, если это полезно, вот суть того, что делает мой взгляд:

def myview(request):

    profile = request.user.profile

    if request.method == 'POST':
        """
        Process the post...
        """
        return HttpResponseRedirect('/memorize/')
    else: # request.method == 'GET'

        ajax = request.GET.has_key('ajax')

        """
        Some irrelevent code...
        """

        if ajax:
            response = HttpResponse()
            profile.get_stack_json(response)
            return response
        else:
            """
            Get data to send along with the content of the page.
            """

        return render_to_response('memorize/memorize.html',
                """ My data """
                context_instance=RequestContext(request))

Спасибо за ваши ответы!

18 ответов


реальное решение

хорошо, мне удалось отследить проблему. Он находится в коде Javascript (как я предложил ниже).

что вам нужно, это:

$.ajaxSetup({ 
     beforeSend: function(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;
         }
         if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
             // Only send the token to relative URLs i.e. locally.
             xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
         }
     } 
});

вместо кода, размещенного в официальных документах: http://docs.djangoproject.com/en/1.2/ref/contrib/csrf/#ajax

рабочий код, происходит от этой записи Django: http://www.djangoproject.com/weblog/2011/feb/08/security/

таким образом, общее решение: "используйте обработчик ajaxSetup вместо обработчика ajaxSend". Я не знаю, почему это работает. Но это работает для меня :)

предыдущий пост (без ответа)

Я испытываю ту же проблему на самом деле.

это происходит после обновления до Django 1.2.5 - в Django 1.2.4 не было ошибок с запросами AJAX POST (AJAX не был защищен в в любом случае, но это сработало просто отлично).

как и OP, я попробовал фрагмент JavaScript, опубликованный в документации Django. Я использую jQuery 1.5. Я также использую " django.промежуточное программное обеспечение.CSRF-атаку.CsrfViewMiddleware " промежуточное ПО.

Я попытался следовать коду промежуточного программного обеспечения, и я знаю, что это не удается:

request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')

а то

if request_csrf_token != csrf_token:
    return self._reject(request, REASON_BAD_TOKEN)

это" если "истинно, потому что" request_csrf_token " пуст.

в основном это означает, что заголовок не ставить. Так что что-то не так с этой линией JS:

xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));

?

Я надеюсь, что предоставленная информация поможет нам в решении вопроса :)


Если вы используете $.ajax функция, вы можете просто добавить csrf маркер в теле данных:

$.ajax({
    data: {
        somedata: 'somedata',
        moredata: 'moredata',
        csrfmiddlewaretoken: '{{ csrf_token }}'
    },

добавьте эту строку в код jQuery:

$.ajaxSetup({
  data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});

и сделал.


проблема в том, что django ожидает, что значение из файла cookie будет возвращено как часть данных формы. Код из предыдущего ответа получает javascript, чтобы выследить значение cookie и поместить его в данные формы. Это прекрасный способ сделать это с технической точки зрения, но это выглядит немного многословен.

в прошлом я делал это более просто, получая javascript, чтобы поместить значение токена в данные post.

Если вы используете {% csrf_token %} в вашем шаблоне вы получите скрытое поле формы, которое несет значение. Но если вы используете {{ csrf_token }}, вы просто получите голое значение токена,поэтому вы можете использовать это в javascript....

csrf_token = "{{ csrf_token }}";

затем вы можете включить это, с требуемым именем ключа в хэше, который вы затем отправляете в качестве данных вызову ajax.


на {% csrf_token %} поместите шаблоны html внутри <form></form>

переводится как-то так:

<input type='hidden' name='csrfmiddlewaretoken' value='Sdgrw2HfynbFgPcZ5sjaoAI5zsMZ4wZR' />

так почему бы просто не grep его в вашем JS, как это:

token = $("#change_password-form").find('input[name=csrfmiddlewaretoken]').val()

и затем передайте его e.g делает какой-то пост, например:

$.post( "/panel/change_password/", {foo: bar, csrfmiddlewaretoken: token}, function(data){
    console.log(data);
});

Если ваша форма правильно публикуется в Django без JS, вы сможете постепенно улучшить ее с помощью ajax без взлома или грязной передачи токена csrf. Просто сериализуйте всю форму, и это автоматически подберет все ваши поля формы в том числе скрытое поле csrf:

$('#myForm').submit(function(){
    var action = $(this).attr('action');
    var that = $(this);
    $.ajax({
        url: action,
        type: 'POST',
        data: that.serialize()
        ,success: function(data){
            console.log('Success!');
        }
    });
    return false;
});

я протестировал это с Django 1.3+ и jQuery 1.5+. Очевидно, что это будет работать для любой формы HTML, а не только для приложений Django.


не-jquery ответ:

var csrfcookie = function() {
    var cookieValue = null,
        name = 'csrftoken';
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
};

использование:

var request = new XMLHttpRequest();
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.setRequestHeader('X-CSRFToken', csrfcookie());
request.onload = callback;
request.send(data);

принятый ответ, скорее всего, является отвлекающим маневром. Разница между Django 1.2.4 и 1.2.5 заключалась в требовании токена CSRF для запросов AJAX.

Я столкнулся с этой проблемой на Django 1.3, и это было вызвано тем, что файл cookie CSRF не установлен в первую очередь. Django не будет устанавливать cookie, если это не нужно. Таким образом, исключительно или сильно ajax-сайт, работающий на Django 1.2.4, потенциально никогда не отправил бы токен клиенту, а затем обновление требование токена приведет к ошибкам 403.

идеальное исправление здесь: http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#page-uses-ajax-without-any-html-form
но вам придется подождать 1.4, если это не просто документация, догоняющая код

редактировать

обратите внимание также, что более поздние документы Django отмечают ошибку в jQuery 1.5, поэтому убедитесь, что вы используете 1.5.1 или более позднюю версию с предложенным Django код: http://docs.djangoproject.com/en/1.3/ref/contrib/csrf/#ajax


используйте Firefox с Firebug. Откройте вкладку "консоль" при запуске запроса ajax. С DEBUG=True вы получаете хорошую страницу ошибок django в качестве ответа, и вы даже можете увидеть отображаемый html ответа ajax на вкладке консоли.

тогда вы узнаете, что такое ошибка.


Я только что столкнулся с немного другой, но похожей ситуацией. Не 100% уверен, что это будет разрешение вашего дела, но я решил проблему для Django 1.3, установив параметр POST "csrfmiddlewaretoken" с правильной строкой значения cookie, которая обычно возвращается в форме вашего домашнего HTML системой шаблонов Django с тегом " {%csrf_token%}". Я не пробовал на старом Django, просто случилось и решилось на Django1.3. Моя проблема заключалась в том, что первый запрос, отправленный через Ajax из формы было успешно сделано, но вторая попытка из того же самого из неудачного, привела к состоянию 403, даже если заголовок "X-CSRFToken" правильно помещен со значением токена CSRF, а также в случае первой попытки. Надеюсь, это поможет.

с уважением,

Хиро


вы можете вставить этот js в свой html-файл, помните, положить его перед другой функцией js

<script>
  // using jQuery
  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 csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
  }

  $(document).ready(function() {
    var csrftoken = getCookie('csrftoken');
    $.ajaxSetup({
      beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
          xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
      }
    });
  });
</script>

кажется, никто не упоминал, как это сделать в чистом JS, используя и {{ csrf_token }}, Так что вот простое решение, где вам не нужно искать через куки или DOM:

var xhttp = new XMLHttpRequest();
xhttp.open("POST", url, true);
xhttp.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
xhttp.send();

один токен CSRF присваивается каждому сеансу (т. е. каждый раз, когда вы входите в систему). Поэтому, прежде чем вы захотите получить некоторые данные, введенные пользователем, и отправить этот вызов ajax какой-либо функции, защищенной csrf_protect decorator, попробуйте найти функции, которые вызываются, прежде чем вы получаете эти данные от пользователя. Е. Г. некоторые шаблоны должны быть оказаны на котором пользователь вводит данные. Этот шаблон отображается какой-то функцией. В этой функции вы можете получить токен csrf как следует: csrf = запрос.Печенье ['csrftoken'] Теперь передайте это значение csrf в контекстном словаре, против которого отображается соответствующий шаблон. Теперь в этом шаблоне напишите эту строку: Теперь в вашей функции javascript, прежде чем сделать запрос ajax, напишите это: var csrf = $('#csrf').val () это выберет значение токена, переданного шаблону, и сохранит его в переменной csrf. Теперь, делая вызов ajax, в ваших данных post передайте это значение : "csrfmiddlewaretoken": csrf

Это работайте, даже если вы не реализуете формы django.

на самом деле, логика здесь : вам нужен токен, который вы можете получить из запроса. Поэтому вам просто нужно выяснить, что функция вызывается сразу после входа в систему. Как только у вас есть этот токен, сделайте еще один вызов ajax, чтобы получить его, или передайте его в какой-то шаблон, доступный вашему ajax.


для тех, кто сталкивается с этим и пытается отладить:

1) проверка django csrf (предполагая, что вы отправляете один) -здесь

2) в моем случае, settings.CSRF_HEADER_NAME был установлен HTTP_X_CSRFTOKEN и мой вызов AJAX посылает заголовок с именем 'HTTP_X_CSRF_TOKEN' так это не работает. Я мог бы изменить его в вызове AJAX или настройке django.

3) Если вы решите изменить его на стороне сервера, найдите место установки django и бросьте точка останова в csrf middleware.если вы используете virtualenv, это будет что-то вроде: ~/.envs/my-project/lib/python2.7/site-packages/django/middleware/csrf.py

import ipdb; ipdb.set_trace() # breakpoint!!
if request_csrf_token == "":
    # Fall back to X-CSRFToken, to make things easier for AJAX,
    # and possible for PUT/DELETE.
    request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')

затем, убедитесь, что csrf токен правильно получен из запроса.Мета!--11-->

4) Если вам нужно изменить заголовок и т. д. - измените эту переменную в файле настроек


Если кто-то борется с axios, чтобы сделать эту работу, это помогло мне:

import axios from 'axios';

axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.xsrfHeaderName = 'X-CSRFToken'

источник:https://cbuelter.wordpress.com/2017/04/10/django-csrf-with-axios/


в моем случае проблема была с конфигурацией nginx, которую я скопировал с основного сервера на временный с отключением https, который не нужен на втором в процессе.

мне пришлось закомментировать эти две строки в config, чтобы заставить его работать снова:

# uwsgi_param             UWSGI_SCHEME    https;
# uwsgi_pass_header       X_FORWARDED_PROTO;

вот менее подробное решение, предоставленное Django:

<script type="text/javascript">
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// set csrf header
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

// Ajax call here
$.ajax({
    url:"{% url 'members:saveAccount' %}",
    data: fd,
    processData: false,
    contentType: false,
    type: 'POST',
    success: function(data) {
        alert(data);
        }
    });
</script>

источник:https://docs.djangoproject.com/en/1.11/ref/csrf/


как нигде не указано в текущих ответах, самое быстрое решение, если вы не врезать js в ваш шаблон:

поставить <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script> перед вашей ссылкой на скрипт.JS-файл в вашем шаблоне, затем добавьте csrfmiddlewaretoken в своем data словарь в вашем js-файле:

$.ajax({
            type: 'POST',
            url: somepathname + "do_it/",
            data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
            success: function() {
                console.log("Success!");
            }
        })