Django CSRF-Prüfung schlägt mit einer Ajax-POST-Anfrage fehl

Lesezeit: 7 Minuten

Benutzer-Avatar
Feuerbusch

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

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

Ich habe den AJAX-Beispielcode kopiert, den sie genau auf dieser Seite haben:

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

Ich habe eine Warnung gesetzt, die den Inhalt von druckt getCookie('csrftoken') Vor dem xhr.setRequestHeader Anruf 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 ich bin ermutigt, dass es etwas findet und sendet.

Aber Django lehnt meinen AJAX-Beitrag immer noch 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 /speichern/HTTP/1.1” 403 2332

Ich bin sicher, dass ich etwas vermisse, und vielleicht ist es einfach, aber ich weiß nicht, was es ist. Ich habe in SO gesucht und einige Informationen zum Deaktivieren der CSRF-Prüfung für meine Ansicht über die gefunden csrf_exempt Dekorateur, aber das finde ich unattraktiv. Ich habe das ausprobiert und es funktioniert, aber ich möchte lieber, dass mein POST so funktioniert, wie Django es erwartet, wenn möglich.

Nur für den Fall, dass es hilfreich ist, hier ist der Kern dessen, was meine Ansicht tut:

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 eure Antworten!

  • Welche Django-Version verwendest du?

    – zQuadrat

    24. Februar 2011 um 7:46 Uhr

  • Haben Sie die richtigen CSRF-Middleware-Klassen hinzugefügt und in die richtige Reihenfolge gebracht?

    – darren

    24. Februar 2011 um 7:57 Uhr

  • Jakub hat meine Frage unten beantwortet, aber nur für den Fall, dass es für andere Leute nützlich ist: @zsquare: Version 1.2.3. @mongoose_za: Ja, sie werden hinzugefügt und in der richtigen Reihenfolge.

    – Feuerbusch

    26. Februar 2011 um 4:42 Uhr


Benutzer-Avatar
Bryan

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

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

  • Wenn ich die markierte Antwort verwende, funktioniert es für mich, aber wenn ich Ihre Lösung hier verwende, funktioniert es nicht. Aber Ihre Lösung sollte funktionieren, ich verstehe nicht, warum es nicht funktioniert. Gibt es noch etwas, das in Django 1.4 getan werden muss?

    – Homann

    26. Mai 2012 um 23:15 Uhr


  • Vielen Dank! so einfach. Funktioniert immer noch auf Django 1.8 und Jquery 2.1.3

    – Alejandro Veintimilla

    21. April 2015 um 19:15 Uhr

  • Diese Lösung erfordert, dass das Javascript in die Vorlage eingebettet wird, oder?

    – Mox

    27. Mai 2015 um 6:28 Uhr

  • @Mox: Fügen Sie dies in HTML ein, aber über Ihrer Js-Datei, wo sich eine Ajax-Funktion befindet <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>

    – Hier hier

    28. Juli 2015 um 14:45 Uhr

  • Danke, Bro. Dies ist die einfachste und sauberste Lösung. Hat sogar auf Django 3 wie ein Zauber funktioniert.

    – Smit Patel

    2. September 2020 um 10:53 Uhr


Benutzer-Avatar
Jakub Gocławski

Echte Lösung

Ok, ich konnte dem Problem auf die Spur kommen. Es liegt im Javascript-Code (wie ich unten vorgeschlagen habe).

Was Sie brauchen, ist dies:

$.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:
https://docs.djangoproject.com/en/2.2/ref/csrf/

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

Die allgemeine Lösung lautet also: “Verwenden Sie den AjaxSetup-Handler anstelle des AjaxSend-Handlers”. Ich weiß nicht, warum es funktioniert. Aber bei mir funktioniert es 🙂

Vorheriger Beitrag (ohne Antwort)

Ich habe tatsächlich das gleiche Problem.

Es tritt nach dem Update auf Django 1.2.5 auf – es gab keine Fehler mit AJAX-POST-Anforderungen in Django 1.2.4 (AJAX war in keiner Weise geschützt, aber es funktionierte einwandfrei).

Genau wie 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 etwas mit dieser JS-Zeile nicht:

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

?

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

  • Das hat funktioniert! Ich habe die .ajaxSetup-Funktion so eingefügt, wie Sie sie oben eingefügt haben, und ich kann jetzt ohne 403-Fehler posten. Vielen Dank für das Teilen der Lösung, Jakub. Guter Fund. 🙂

    – Feuerbusch

    26. Februar 2011 um 4:38 Uhr

  • Verwenden ajaxSetup statt ajaxSend läuft den jQuery-Dokumenten zuwider: api.jquery.com/jQuery.ajaxSetup

    – Markus Lavin

    9. Mai 2011 um 14:07 Uhr


  • Mit 1.3 hat der offizielle Django-Dokumentationseintrag für mich funktioniert.

    – Mönchskutte

    15. August 2011 um 14:22 Uhr

  • Ich habe es versucht, aber das scheint bei mir nicht zu funktionieren, ich verwende jQuery v1.7.2, meine Frage ist stackoverflow.com/questions/11812694/…

    – Tagträumer

    4. August 2012 um 23:56 Uhr

  • Ich muss eine Anmerkung hinzufügen @ensure_csrf_cookie um meine Ansichtsfunktion zu erzwingen csrf Cookie, wenn die Seite von mobilen Geräten angefordert wird.

    – Kane

    22. Januar 2014 um 7:12 Uhr

Benutzer-Avatar
Kambiz

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

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

und fertig.

  • Ich habe das versucht, außer dass mein Formular einen Datei-Upload hat. Mein Backend ist Django und erhalte immer noch Fehler 400 CSRF Failed: CSRF token missing or incorrect.

    – Hussain

    12. Oktober 2018 um 4:35 Uhr


  • @Hussain versuchen Sie hinzuzufügen {% csrf_token %} irgendwo in der HTML-Vorlage, auch wenn keine Formular-Tags vorhanden sind. Ohne sie tritt zumindest bei mir der 403-Fehler auf. Siehe: stackoverflow.com/a/65757544/7076615 für weitere Informationen, und ich empfehle auch die Verwendung von X-editable mit Django, es ist ganz nett und in diesem Thread ist ein vollständiges Beispiel.

    – Prime-Gap-Homologie-Stoner-Mon.

    17. Januar 2021 um 5:12 Uhr

  • Wie könnten wir dasselbe in Vanilla JavaScript machen?

    – Ionekum

    29. September 2021 um 3:38 Uhr

Das Problem liegt darin, dass Django erwartet, dass der Wert des Cookies als Teil der Formulardaten zurückgegeben wird. Der Code aus der vorherigen Antwort erhält Javascript, um den Cookie-Wert zu finden und in die Formulardaten einzufügen. Das ist aus technischer Sicht eine schöne Art, es zu tun, aber es sieht ein bisschen ausführlich aus.

In der Vergangenheit habe ich es einfacher gemacht, indem ich das Javascript dazu gebracht habe, den Token-Wert in die Post-Daten einzufügen.

Wenn Sie {% csrf_token %} in Ihrer Vorlage verwenden, wird ein verstecktes Formularfeld ausgegeben, das den Wert trägt. Aber wenn Sie {{ csrf_token }} verwenden, erhalten Sie nur den bloßen Wert des Tokens, sodass Sie dies in Javascript so 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 senden.

Das {% csrf_token %} Fügen Sie HTML-Vorlagen hinzu <form></form>

übersetzt so etwas wie:

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

warum gep es also nicht einfach so in dein JS ein:

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

und übergeben Sie es dann, indem Sie z. B. einen POST ausführen, wie:

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

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

Benutzer-Avatar
Alex

Es scheint, dass niemand erwähnt hat, wie man dies in reinem JS mit dem macht X-CSRFToken Kopfzeile u {{ csrf_token }}hier ist also eine einfache Lösung, bei der Sie die Cookies oder das DOM nicht durchsuchen müssen:

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

1037950cookie-checkDjango CSRF-Prüfung schlägt mit einer Ajax-POST-Anfrage fehl

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy