Ошибка Django CSRF с запросом POJ Ajax

Я могу использовать некоторую помощь, связанную с механизмом защиты Django CSRF, через мой пост 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, и оно действительно заполнено некоторыми данными. Я не уверен, как проверить правильность токена, но я рад, что он нашел и отправил что-то.

Но Django по-прежнему отклоняет мой пост 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/запомнить/HTTP/1.1" 403 2332

Я уверен, что мне что-то не хватает, и, может быть, это просто, но я не знаю, что это такое. Я искал вокруг SO и увидел некоторую информацию об отключении проверки CSRF для моего представления с помощью декоратора csrf_exempt, но я считаю, что это непривлекательно. Я пробовал это, и это работает, но я предпочел бы, чтобы мой POST работал так, как 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))

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

Ответ 1

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

Хорошо, мне удалось отследить проблему. Он лежит в коде 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'));
         }
     } 
});

вместо кода, опубликованного в официальных документах: https://docs.djangoproject.com/en/2.2/ref/csrf/

Рабочий код взят из этой записи 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.middleware.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'));

?

Я надеюсь, что предоставленные детали помогут нам в решении проблемы :)

Ответ 2

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

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

Ответ 3

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

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

и сделано.

Ответ 4

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

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

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

csrf_token = "{{ csrf_token }}";

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

Ответ 5

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

переводит на что-то вроде:

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

так почему бы не просто вставить его в JS следующим образом:

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

а затем передать его, например, выполнить POST, например:

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

Ответ 6

Если ваша форма правильно помещается в 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.

Ответ 7

Не-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);

Ответ 8

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

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

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

Edit

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

Ответ 9

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

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

Ответ 10

Кажется, никто не упомянул, как это сделать в чистом JS, используя заголовок X-CSRFToken и {{ csrf_token }}, поэтому здесь простое решение, в котором вам не нужно искать файлы cookie или DOM:

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

Ответ 11

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

Привет,

Хиро

Ответ 12

вы можете вставить этот 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>

Ответ 13

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

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

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

Ответ 14

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

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

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

3) Если вы решите изменить его на стороне сервера, найдите свое место установки django и бросьте точку останова в csrf middleware.f, вы используете 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 правильно получен из запроса. META

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

Ответ 15

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

import axios from 'axios';

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

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

Ответ 16

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

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

# uwsgi_param             UWSGI_SCHEME    https;
# uwsgi_pass_header       X_FORWARDED_PROTO;

Ответ 17

Вот менее подробное решение, предоставленное 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/

Ответ 18

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

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

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