it-swarm.com.de

Django CSRF-Prüfung schlägt mit einem Ajax fehl POST anfordern

Ich könnte Hilfe bei der Einhaltung des CSRF-Schutzmechanismus von Django über meinen AJAX Beitrag gebrauchen. Ich bin hier den Anweisungen gefolgt:

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

Ich habe den AJAX Beispielcode genau auf dieser Seite kopiert:

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

Ich habe vor dem xhr.setRequestHeader-Aufruf eine Warnung ausgegeben, die den Inhalt von getCookie('csrftoken') druckt, und es ist tatsächlich mit einigen Daten gefüllt. Ich bin mir nicht sicher, wie ich überprüfen kann, ob das Token korrekt ist, aber es wird mir empfohlen, etwas zu finden und zu senden.

Aber Django lehnt immer noch meinen AJAX Post ab.

Hier ist mein JavaScript:

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

Hier ist der Fehler, den ich von Django sehe:

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

Ich bin sicher, dass mir etwas fehlt, und vielleicht ist es einfach, aber ich weiß nicht, was es ist. Ich habe herumgesucht SO und habe einige Informationen zum Deaktivieren der CSRF-Prüfung für meine Ansicht über den csrf_exempt-Dekorator gesehen, aber das finde ich nicht ansprechend. Ich habe das ausprobiert und es funktioniert, aber ich würde lieber POST so arbeiten, wie Django es erwartet, um es zu erwarten, wenn möglich.

Nur für den Fall, dass es hilfreich ist, hier ist das Wesentliche meiner Sichtweise:

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))

Danke für Ihre Antworten!

158
firebush

Echte Lösung

Ok, ich konnte das Problem aufspüren. Es liegt im Javascript-Code (wie ich unten vorgeschlagen habe).

Was Sie brauchen, ist folgendes:

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

anstelle des in den offiziellen Dokumenten veröffentlichten Codes: http://docs.djangoproject.com/de/1.2/ref/contrib/csrf/#ajax

Der Arbeitscode stammt aus diesem Django-Eintrag: http://www.djangoproject.com/weblog/2011/feb/08/security/

Die allgemeine Lösung lautet also: "Verwenden Sie ajaxSetup-Handler anstelle von ajaxSend-Handler". Ich weiß nicht, warum es funktioniert. Aber es funktioniert bei mir :)

Vorheriger Beitrag (ohne Antwort)

Ich habe tatsächlich das gleiche Problem.

Es tritt nach dem Update auf Django 1.2.5 auf - in Django 1.2.4 gab es keine Fehler bei AJAX POST -Anfragen (AJAX war in keiner Weise geschützt, aber es hat gut funktioniert).

Genau wie bei OP habe ich das in der Django-Dokumentation veröffentlichte JavaScript-Snippet ausprobiert. Ich verwende jQuery 1.5. Ich verwende auch die Middleware "Django.middleware.csrf.CsrfViewMiddleware".

Ich habe versucht, dem Middleware-Code zu folgen, und ich weiß, dass dies fehlschlägt:

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

und dann

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

dieses "if" ist wahr, weil "request_csrf_token" leer ist.

Grundsätzlich bedeutet dies, dass der Header NICHT gesetzt ist. Stimmt also nichts mit dieser JS-Linie:

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

?

Ich hoffe, dass die bereitgestellten Details uns bei der Lösung des Problems helfen werden :)

162

Wenn Sie die $.ajax-Funktion verwenden, können Sie einfach das csrf-Token im Datenkörper hinzufügen:

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

Fügen Sie diese Zeile Ihrem jQuery-Code hinzu:

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

und fertig.

70
Kambiz

Das Problem ist, dass Django erwartet, dass der Wert des Cookies als Teil der Formulardaten zurückgegeben wird. Der Code aus der vorherigen Antwort enthält Javascript, mit dem der Cookie-Wert ermittelt und in die Formulardaten eingefügt werden kann. Das ist aus technischer Sicht ein reizvoller Weg, aber es sieht ein bisschen wortreich aus.

In der Vergangenheit habe ich es einfacher gemacht, indem ich das Javascript dazu brachte, den Tokenwert in die Postdaten zu setzen.

Wenn Sie in Ihrer Vorlage {% csrf_token%} verwenden, wird ein ausgeblendetes Formularfeld ausgegeben, das den Wert enthält. Wenn Sie jedoch {{csrf_token}} verwenden, erhalten Sie nur den bloßen Wert des Tokens, sodass Sie diesen in Javascript wie folgt verwenden können.

csrf_token = "{{ csrf_token }}";

Dann können Sie das mit dem erforderlichen Schlüsselnamen in den Hash aufnehmen, den Sie dann als Daten an den Ajax-Aufruf übergeben.

14
fatgeekuk

Der {% csrf_token %} fügt HTML-Vorlagen in <form></form> ein. 

übersetzt in etwas wie:

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

also warum grep es nicht einfach in deinem JS so:

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

und dann übergeben Sie es, indem Sie einige POST ausführen, wie:

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

Wenn Ihr Formular in Django ohne JS korrekt gepostet wird, sollten Sie es mit ajax schrittweise verbessern können, ohne das csrf-Token zu hacken oder zu beschädigen. Serialisieren Sie einfach das gesamte Formular, und automatisch werden alle Ihre Formularfelder einschließlich das ausgeblendete csrf-Feld abgerufen:

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

Ich habe dies mit Django 1.3+ und jQuery 1.5+ getestet. Dies funktioniert natürlich für jedes HTML-Formular, nicht nur für Django-Apps.

8
GivP

Nicht-Jquery-Antwort:

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;
};

verwendungszweck:

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);
7
user1428660

Die akzeptierte Antwort ist höchstwahrscheinlich ein roter Hering. Der Unterschied zwischen Django 1.2.4 und 1.2.5 bestand in der Anforderung eines CSRF-Tokens für AJAX Anforderungen.

Ich bin auf dieses Problem bei Django 1.3 gestoßen und es wurde dadurch verursacht, dass das CSRF-Cookie nicht gesetzt wurde. Django setzt das Cookie nicht, es sei denn, es muss. Eine exklusiv oder stark ajax-basierte Site, die unter Django 1.2.4 ausgeführt wird, hätte möglicherweise niemals ein Token an den Client gesendet, und das Upgrade, das das Token erfordert, würde dann die 403-Fehler verursachen.

Die ideale Lösung finden Sie hier: http://docs.djangoproject.com/de/dev/ref/contrib/csrf/#page-uses-ajax-without-any-html-form
aber Sie müssten auf 1.4 warten, es sei denn, dies ist nur eine Dokumentation, die den Code einholt

Bearbeiten

Beachten Sie auch, dass die späteren Django Dokumente einen Fehler in jQuery 1.5 aufweisen, stellen Sie also sicher, dass Sie 1.5.1 oder höher mit dem Django vorgeschlagenen Code verwenden: http: // docs .djangoproject.com/de/1.3/ref/contrib/csrf/# ajax

5
Steven

Verwenden Sie Firefox mit Firebug. Öffnen Sie die Registerkarte "Konsole", während Sie die Ajax-Anfrage abfeuern. Mit DEBUG=True erhalten Sie die Nice Django-Fehlerseite als Antwort und Sie können sogar die gerenderte HTML-Datei der Ajax-Antwort auf der Registerkarte "Konsole" sehen.

Dann wissen Sie, was der Fehler ist.

4
jammon

sie können dieses Js in Ihre HTML-Datei einfügen. Denken Sie daran, es vor der anderen JS-Funktion zu setzen

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

2
nooobpan

Es scheint, dass niemand erwähnt hat, wie dies in Pure JS mit dem X-CSRFToken-Header und {{ csrf_token }} zu tun ist. Hier eine einfache Lösung, bei der Sie nicht durch die Cookies oder das DOM suchen müssen:

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

Ich bin gerade auf eine etwas andere, aber ähnliche Situation gestoßen. Nicht ganz sicher, ob es sich um eine Lösung für Ihren Fall handelt, aber ich habe das Problem für Django 1.3 behoben, indem Sie einen Parameter für den POST 'csrfmiddlewaretoken' mit der richtigen Cookie-Wertzeichenfolge gesetzt haben, die normalerweise in der Form Ihres zurückgegeben wird home HTML von Djangos Vorlagensystem mit dem Tag '{% csrf_token%}'. Ich habe nicht den älteren Django ausprobiert, ich bin einfach auf Django1.3 ... gelaufen und habe das Problem gelöst. Mein Problem war, dass die erste Anfrage, die über Ajax eingereicht wurde, erfolgreich ausgeführt wurde, der zweite Versuch jedoch aus dem gleichen Grund fehlgeschlagen ist 403 Status, obwohl der Header 'X-CSRFToken' sowohl beim CSRF-Tokenwert als auch im Fall des ersten Versuchs korrekt platziert wurde. Hoffen Sie, dass dies hilft.

Grüße,

Hiro

2

Wenn jemand mit Axios zu kämpfen hat, um diese Arbeit zu erreichen, hat mir das geholfen:

import axios from 'axios';

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

Quelle: https://cbuelter.wordpress.com/2017/04/18/Django-csrf-with-axios/

1
Mikko Pöri

für jemanden, der das entdeckt und versucht zu debuggen:

1) Der Django-CSRF-Check (vorausgesetzt, Sie senden einen) ist hier

2) In meinem Fall wurde settings.CSRF_HEADER_NAME auf 'HTTP_X_CSRFTOKEN' gesetzt, und mein Aufruf AJAX sendete einen Header mit dem Namen 'HTTP_X_CSRF_TOKEN'. Ich könnte es entweder im Anruf AJAX oder in der Django-Einstellung ändern.

3) Wenn Sie sich dafür entscheiden, es serverseitig zu ändern, suchen Sie nach dem Installationsort von Django und werfen Sie einen Haltepunkt in den csrf middleware. Wenn Sie virtualenv verwenden, ist dies etwa wie folgt: ~/.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, '')

Stellen Sie dann sicher, dass das csrf-Token korrekt aus request.META stammt

4) Wenn Sie den Header ändern müssen, usw. - ändern Sie diese Variable in Ihrer Einstellungsdatei

1
daino3

Jeder Sitzung wird ein CSRF-Token zugewiesen (dh bei jeder Anmeldung). Bevor Sie also einige vom Benutzer eingegebene Daten erhalten und diese als Ajax-Aufruf an eine durch csrf_protect decorator geschützte Funktion senden möchten, suchen Sie nach Die Funktionen, die aufgerufen werden, bevor Sie diese Daten vom Benutzer erhalten. Z.B. Es muss eine Vorlage gerendert werden, für die der Benutzer Daten eingibt. Diese Vorlage wird von einer Funktion gerendert. In dieser Funktion können Sie das Csrf-Token folgendermaßen erhalten: csrf = request.COOKIES [' csrftoken '] Nun übergeben Sie diesen csrf-Wert im Kontextwörterbuch, gegen das die betreffende Vorlage gerendert wird . Schreiben Sie in dieser Vorlage die folgende Zeile: Nun schreiben Sie in Ihre Javascript-Funktion, bevor Sie eine Ajax-Anforderung erstellen : var csrf = $ ('# csrf'). val () Dies wählt den Wert des an template übergebenen Tokens aus und speichert ihn in der Variablen csrf . Wenn Sie nun einen Ajax-Aufruf durchführen, übergeben Sie diesen Wert in Ihren Post-Daten ebenfalls: "csrfmiddlewaretoken": csrf 

Dies funktioniert auch, wenn Sie keine Django-Formulare implementieren.

Die Logik hier ist in der Tat: Sie benötigen ein Token, das Sie von request bekommen können. Sie müssen also nur die Funktion herausfinden, die sofort nach dem Einloggen aufgerufen wird. Sobald Sie dieses Token haben, rufen Sie entweder einen weiteren Ajax-Aufruf auf es oder übergeben es an eine Vorlage, die für Ihren Ajax zugänglich ist.

1

Da es nirgendwo in den aktuellen Antworten angegeben ist, ist die schnellste Lösung, wenn Sie nicht js in Ihre Vorlage einbetten: 

Fügen Sie <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script> vor Ihrem Verweis auf die Datei script.js in Ihre Vorlage ein und fügen Sie csrfmiddlewaretoken in Ihr data-Wörterbuch in Ihre js-Datei ein:

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

Hier ist eine weniger ausführliche Lösung von 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>

Quelle: https://docs.djangoproject.com/de/1.11/ref/csrf/

0
Braden Holt

In meinem Fall lag das Problem bei der Nginx-Konfiguration, die ich vom Hauptserver auf einen temporären Server kopiert habe, wobei https deaktiviert wurde, das für den zweiten Prozess nicht erforderlich ist.

Ich musste diese beiden Zeilen in der config auskommentieren, damit es wieder funktioniert:

# uwsgi_param             UWSGI_SCHEME    https;
# uwsgi_pass_header       X_FORWARDED_PROTO;
0
int_ua