web-dev-qa-db-ger.com

CSRF mit Django, React + Redux mit Axios

Dies ist ein Bildungsprojekt, nicht für die Produktion. Ich hatte nicht vor, Benutzeranmeldungen als Teil davon zu haben.

Kann ich mit einem CSRF-Token POST an Django anrufen, ohne dass sich Benutzer anmelden müssen? Kann ich dies ohne jQuery tun? Ich bin hier aus meiner Tiefe heraus und vermische sicher einige Konzepte.

Für die JavaScript-Seite habe ich dieses redux-csrf -Paket gefunden. Ich bin mir nicht sicher, wie ich es mit meiner POST-Aktion mit Axios kombinieren kann:

export const addJob = (title, hourly, tax) => {
  console.log("Trying to addJob: ", title, hourly, tax)
  return (dispatch) => {
    dispatch(requestData("addJob"));
    return axios({
      method: 'post',
      url: "/api/jobs",
      data: {
        "title": title,
        "hourly_rate": hourly,
        "tax_rate": tax
      },
      responseType: 'json'
    })
      .then((response) => {
        dispatch(receiveData(response.data, "addJob"));
      })
      .catch((response) => {
        dispatch(receiveError(response.data, "addJob"));
      })
  }
};

Auf der Django-Seite habe ich this Dokumentation über CSRF und this über das Arbeiten mit klassenbasierten Ansichten gelesen.

Hier ist meine Ansicht bisher:

class JobsHandler(View):

    def get(self, request):
        with open('./data/jobs.json', 'r') as f:
            jobs = json.loads(f.read())

        return HttpResponse(json.dumps(jobs))

    def post(self, request):
        with open('./data/jobs.json', 'r') as f:
            jobs = json.loads(f.read())

        new_job = request.to_dict()
        id = new_job['title']
        jobs[id] = new_job

        with open('./data/jobs.json', 'w') as f:
            f.write(json.dumps(jobs, indent=4, separators=(',', ': ')))

        return HttpResponse(json.dumps(jobs[id]))

Ich habe versucht, den csrf_exempt -Dekorateur zu verwenden, nur um mich vorerst nicht darum kümmern zu müssen.

Ich habe {% csrf_token %} zu meiner Vorlage hinzugefügt.

Dies ist meine getCookie-Methode (gestohlen von Django-Dokumenten):

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 = cookies[i].trim();
            // 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;
}

Ich habe gelesen, dass ich die Axios CSRF-Informationen ändern muss:

var axios = require("axios");
var axiosDefaults = require("axios/lib/defaults");

axiosDefaults.xsrfCookieName = "csrftoken"
axiosDefaults.xsrfHeaderName = "X-CSRFToken"

Wo stecke ich das eigentliche Token, den Wert, den ich beim Aufruf von getCookie('csrftoken') bekomme?

34
Reed Dunkle

Es gibt drei Möglichkeiten. Sie können das Token manuell in den Header jedes Axios-Aufrufs einschließen, Sie können die xsrfHeaderName von Axios in jedem Aufruf festlegen, oder Sie setzen einen Standardwert für xsrfHeaderName.

1. Manuelles Hinzufügen

Angenommen, Sie haben den Wert des Tokens in einer Variablen namens csrfToken gespeichert. Setzen Sie die Überschriften in Ihrem Axios-Aufruf:

// ...
method: 'post',
url: '/api/data',
data: {...},
headers: {"X-CSRFToken": csrfToken},
// ...

2. Einstellung xsrfHeaderName im Aufruf:

Füge das hinzu:

// ...
method: 'post',
url: '/api/data',
data: {...},
xsrfHeaderName: "X-CSRFToken",
// ...

Fügen Sie dann in Ihrer settings.py-Datei diese Zeile hinzu:

CSRF_COOKIE_NAME = "XSRF-TOKEN"

3. Standardkopfzeilen festlegen[1]

Anstatt den Header in jedem Aufruf zu definieren, können Sie Standardheader für Axios festlegen.

Fügen Sie in der Datei, in die Sie Axios importieren, um den Aufruf durchzuführen, dies unter Ihren Importen hinzu: 

axios.defaults.xsrfHeaderName = "X-CSRFToken";

Fügen Sie dann in Ihrer settings.py-Datei diese Zeile hinzu:

CSRF_COOKIE_NAME = "XSRF-TOKEN"

Edit: Anscheinend funktioniert es bei Safari etwas anders[2]

Edit2: Möglicherweise müssen Sie auch einstellen[3]:

axios.defaults.withCredentials = true

Die Verwirrung:

Django Docs

Erstens, die ganze Passage aus der Django-Dokumentation die James Evans verwiesen :

Legen Sie für jedes XMLHttpRequest einen benutzerdefinierten X-CSRFToken-Header auf .__ fest. Wert des CSRF-Tokens. Dies ist oft einfacher, da viele JavaScript-Dateien verwendet werden Frameworks stellen Hooks zur Verfügung, mit denen Header für jedes .__ gesetzt werden können. anfordern.

Als ersten Schritt müssen Sie das CSRF-Token selbst abrufen. Das Empfohlene Quelle für das Token ist das csrftoken-Cookie, das gesetzt wird, wenn Sie haben den CSRF-Schutz für Ihre Ansichten wie oben beschrieben aktiviert.

Hinweis

Das CSRF-Token-Cookie hat standardmäßig den Namen csrftoken. Sie können jedoch Steuern Sie den Cookie-Namen über die Einstellung CSRF_COOKIE_NAME.

Der Name des CSRF-Headers lautet standardmäßig HTTP_X_CSRFTOKEN. Sie können jedoch Passen Sie es mit der Einstellung CSRF_HEADER_NAME an.


Axios Docs

Dies ist aus den Axios-Dokumenten . Es zeigt an, dass Sie den Namen des Cookies setzen, der die Variable csrftoken enthält, und den Namen der Kopfzeile hier:

  // `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
  xsrfCookieName: 'XSRF-TOKEN', // default

  // `xsrfHeaderName` is the name of the http header that carries the xsrf token value
  xsrfHeaderName: 'X-XSRF-TOKEN', // default

Bedingungen

Wie in meiner Frage angegeben, greifen Sie mit document.cookie auf Cookies zu. Der einzige Cookie, den ich habe, ist der CSRF-Token, den ich in die Django-Vorlage eingefügt habe. Hier ist ein Beispiel:

csrftoken=5knNceCUi9nL669hGGsvCi93XfqNhwTwM9Pev7bLYBOMXGbHVrjitlkKi44CtpFU

In diesen Dokumenten werden einige Konzepte verworfen, die verwirrend werden:

  • Der Name des Cookies, das das CSRF-Token enthält. In Django ist dies standardmäßig csrftoken, das sich auf der linken Seite des Gleichheitszeichens im Cookie befindet.
  • Das eigentliche Token. Dies ist alles auf der rechten Seite des Gleichheitszeichens im Cookie.
  • Der http-Header, der den Tokenwert enthält.

Dinge, die ich ausprobiert habe, haben nicht funktioniert: 1 , 2

30
Reed Dunkle

Ich habe herausgefunden, dass axios.defaults.xsrfCookieName = "XCSRF-TOKEN"; Und CSRF_COOKIE_NAME = "XCSRF-TOKEN"

FUNKTIONIERT NICHT IN Apple Safari unter Mac OS

Die Lösung für MAC Safari ist einfach: ändern Sie einfach XCSRF-TOKEN in csrftoken 

Also, in js-Code sollte sein:

    import axios from 'axios';
    axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
    axios.defaults.xsrfCookieName = "csrftoken";

In Einstellungen.py:

    CSRF_COOKIE_NAME = "csrftoken"
12
yestema

Diese Konfiguration funktioniert für mich ohne Probleme Config axios CSRF Django

import axios from 'axios'

/**
 * Config global for axios/Django
 */
axios.defaults.xsrfHeaderName = "X-CSRFToken"
axios.defaults.xsrfCookieName = 'csrftoken'

export default axios

6
krescruz

Der "leichte Weg" hat für mich fast funktioniert. Das scheint zu funktionieren:

import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "XCSRF-TOKEN";

Und in der Datei settings.py:

CSRF_COOKIE_NAME = "XCSRF-TOKEN"
3
cran_man

Sie können das von Django bereitgestellte CSRF-Token manuell in alle Ihre Post-Requests einfügen, was jedoch ärgerlich ist. 

Aus den Django-Dokumenten

Während die obige Methode (CSRF-Token manuell setzen) für AJAX POST Anforderungen verwendet werden kann, hat sie einige Nachteile: Sie müssen daran denken, das CSRF-Token als POST Daten mit jeder POST Anforderung. Aus diesem Grund gibt es eine alternative Methode: Setzen Sie in jedem XMLHttpRequest einen benutzerdefinierten X-CSRFToken-Header auf den Wert des CSRF-Token. Dies ist oft einfacher, da viele JavaScript-Frameworks Hooks bereitstellen, mit denen Header für jede Anforderung festgelegt werden können.

Die Dokumente enthalten Code, den Sie verwenden können, um das CSRF-Token aus dem CSRF-Token-Cookie zu ziehen und dann dem Header Ihrer AJAX Anforderung hinzuzufügen. 

1
James Evans

Es gibt tatsächlich einen wirklich einfachen Weg, dies zu tun.

Fügen Sie axios.defaults.xsrfHeaderName = "X-CSRFToken"; zu Ihrer App-Konfiguration hinzu und legen Sie dann CSRF_COOKIE_NAME = "XSRF-TOKEN" in Ihrer Datei settings.py fest. Klappt wunderbar.

1
Dave Merwin

Für mich hörte Django nicht auf die Header, die ich sendete. Ich konnte mich in die API einrollen, konnte aber nicht mit Axios darauf zugreifen. Schauen Sie sich das Paket cors-headers an ... es könnte Ihr neuer bester Freund sein.

Ich habe das Problem behoben, indem ich Django-Cors-Header installiert habe

pip install Django-cors-headers

Und dann hinzufügen 

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

und 

MIDDLEWARE = [  # Or MIDDLEWARE_CLASSES on Django < 1.10
    ...
    'corsheaders.middleware.CorsMiddleware',
    'Django.middleware.common.CommonMiddleware',
    ...
]

in meine Einstellungen.py

Ich hatte auch

ALLOWED_HOSTS = ['*']
CORS_Origin_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_EXPOSE_HEADERS = (
    'Access-Control-Allow-Origin: *',
)

in meinen settings.py obwohl das wahrscheinlich übertrieben ist

1
spinach

Zusätzlich zu dem, was yestema gesagt hat (und von krescruz, cran_man, Dave Merwin ua) wiederholt wird, benötigen Sie auch:

axios.defaults.withCredentials = true
0
sureshvv