Django-rest-framework: DRF sollte standardmäßig alle OPTIONS-Anfragen autorisieren

Erstellt am 21. Nov. 2017  ·  22Kommentare  ·  Quelle: encode/django-rest-framework

Im Anschluss an die Diskussion zu #908

Die Berechtigungen für OPTIONS (Metadaten)-Anfragen werden in DRF falsch behandelt.

Gemäß den OPTIONS Preflight-Anfragen NICHT authentifiziert, was bedeutet, dass die Benutzer beim Preflighten einer Anforderung für authentifizierte Endpunkte immer einen 401-Fehler erhalten, da moderne Browser dies nie gemäß den Spezifikationen senden:

Stellen Sie andernfalls eine Preflight-Anfrage. Rufen Sie die Anforderungs-URL vom Ursprungsquellenursprung ab, indem Sie die Referrer-Quelle als Überschreibungs-Referrer-Quelle mit dem manuellen Weiterleitungs-Flag und dem Block-Cookies-Flag gesetzt verwenden, mit der Methode OPTIONS und mit den folgenden zusätzlichen Einschränkungen:

  • Fügen Sie einen Access-Control-Request-Method-Header mit dem Header-Feldwert der Anfragemethode ein (auch wenn dies eine einfache Methode ist).
  • Wenn die Kopfzeilen der Autorenanfrage nicht leer sind, fügen Sie eine Kopfzeile für Access-Control-Request-Headers mit als Kopfzeilenfeldwert eine durch Kommas getrennte Liste der Kopfzeilenfeldnamen aus den Kopfzeilen der Autorenanfrage in lexikographischer Reihenfolge ein, die jeweils in ASCII-Kleinbuchstaben umgewandelt sind (auch wenn eins oder mehr sind eine einfache Kopfzeile).
  • Schließen Sie die Kopfzeilen der Autorenanfrage aus.
  • ➡️ Benutzeranmeldeinformationen ausschließen .
  • Schließen Sie den Körper der Anforderungsentität aus.

Ich denke, das sollte hier das Standardverhalten sein.
DRF sollte standardmäßig alle OPTIONS Anforderungen für Standardberechtigungsklassen (IsAuthenticated, IsAdminUser usw.) autorisieren und Benutzer können dies überschreiben, wenn sie Metadateninformationen explizit schützen müssen (was die standardmäßige CORS-Kompatibilität verletzt).

Schritte zum Reproduzieren :

ansichten.py

class MyView(APIView):

    permission_classes = (IsAuthenticated,)

urls.py

urlpatterns = [
    url(r'^myview/', MyView.as_view()),
]

Konsole

http GET http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

http OPTIONS http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

Erwartetes Verhalten

http GET http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

http OPTIONS http://127.0.0.1:8000/myview/
HTTP/1.0 200 OK

Temporäre Problemumgehung

class IsAuthenticated(permissions.IsAuthenticated):

    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        return super(IsAuthenticated, self).has_permission(request, view)

Hilfreichster Kommentar

DRF-Version: 3.7.3
Python 3.6.3

Der oben erwähnte temporäre Workaround hat bei mir nicht funktioniert. Alle Anfragen wurden authentifiziert, unabhängig davon, ob es sich um PUT, POST, GET, OPTIONS usw. handelte.

Es hat daran gearbeitet, es zu ändern:

# myapp/permissions.py
from rest_framework.permissions import IsAuthenticated

class AllowOptionsAuthentication(IsAuthenticated):
    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        return request.user and request.user.is_authenticated

Und in settings.py:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.TokenAuthentication',),
    'DEFAULT_PERMISSION_CLASSES': (
        'myapp.permissions.AllowOptionsAuthentication',
    )
}

Alle 22 Kommentare

DRF-Version: 3.7.3
Python 3.6.3

Der oben erwähnte temporäre Workaround hat bei mir nicht funktioniert. Alle Anfragen wurden authentifiziert, unabhängig davon, ob es sich um PUT, POST, GET, OPTIONS usw. handelte.

Es hat daran gearbeitet, es zu ändern:

# myapp/permissions.py
from rest_framework.permissions import IsAuthenticated

class AllowOptionsAuthentication(IsAuthenticated):
    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        return request.user and request.user.is_authenticated

Und in settings.py:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.TokenAuthentication',),
    'DEFAULT_PERMISSION_CLASSES': (
        'myapp.permissions.AllowOptionsAuthentication',
    )
}

Ich bin auch auf das gleiche Problem gestoßen. Ich stimme der vorgeschlagenen Lösung zu und danke für den vorübergehenden Workaround!

Nicht zu 100% überzeugt, dass "sollte alle OPTIONS-Anfragen standardmäßig autorisieren" genau das ist, was wir wollen, da es viele Entwickler gibt, die das REST-Framework verwenden, die die OPTIONS Anfragen für andere Fälle als CORS-Preflight-Anfragen verwenden.

Ich denke jedoch, dass wir wahrscheinlich eine gesegnete CORS-Option haben möchten. Ich weiß nicht, ob das bedeutet, dass wir https://github.com/ottoyiu/django-cors-headers/ direkt

Wird dieses Problem behoben, sobald dieses Paket verwendet wurde?

Dieses Paket ist nicht wirklich die Lösung, es hängt nur CORS-Header an, behebt jedoch nicht die Tatsache, dass OPTIONS HTTP401 zurückgibt, wenn die DRF-Berechtigung nicht speziell für alle nicht authentifizierten Anforderungen erteilt wurde.

IMO sollte es umgekehrt eingeführt werden, dh standardmäßig mit einer Breaking-Change-Mitteilung erlaubt.
Dies ist das erwartete Verhalten gemäß den W3C-Spezifikationen, und die Erwähnung eines Drittanbieters im Dokument zum Patchen einer falschen Implementierung ist keine wirklich saubere Lösung ...

Obwohl es Leute gibt, die OPTIONS für nicht-CORS-bezogene Dinge verwenden (ich bin einer von ihnen), gibt es möglicherweise viel mehr implizit, dass Dinge sofort funktionieren, da CORS-Mechanismen von immer mehr Browsern durchgesetzt werden. Tatsächlich generieren diese Mechanismen potenziell über 99% der OPTIONS-Anfragen über das Web.

Interessanter Konflikt. Ich verstehe das Problem mit der standardmäßigen impliziten Zulassung für die OPTIONS-Methode völlig. Ich bin natürlich eher voreingenommen in Richtung einer sichereren Lösung und ich nehme an, es hängt vom Zweck des Entwicklers für DRF ab. Daher scheint mir eine Standardverweigerung angemessener zu sein.

Es ist auch möglich, dass die W3C-Spezifikation zum Zulassen von OPTIONS nur im Kontext von CORS gilt. Offensichtlich hat DRF keine Möglichkeit, den Kontext zu kennen und sollte eine Einstellung für den Entwickler sein.

Wenn CORS vorgesehen ist, lassen Sie alle OPTIONS-Anforderungen zu. CORS ist standardmäßig unbeabsichtigt.

Eigentlich denke ich, dass wir einen Breaking Change für eine Hauptversion in Betracht ziehen könnten, wobei das aktuelle Verhalten verfügbar bleibt. Unser bestehendes OPTIONS-Verhalten ist vorhanden, seit die Leute JSONP anstelle von CORS verwenden würden .

+1

@tomchristie haben Sie eine Ahnung, wann ein Update / Fix für die Hauptversion stattfinden würde? Ich spreche auch dieses Thema an.

Der Workaround von

Dies ist ein Meilenstein, um sicherzustellen, dass 3.9 . richtig berücksichtigt wird

Eine alternative Problemumgehung besteht darin, dies explizit in der Ansicht zu behandeln. Für den Fall, dass Sie mehrere Berechtigungsklassen haben, ist dies eher DRY, da jede von ihnen diese Behandlung benötigen würde.

def check_permissions(self, request):
        if request.method == 'OPTIONS':
            return
        super(MyApiView, self).check_permissions(request)

Um darauf zurückzukommen, nachdem wir uns genauer damit befasst haben - das Paket https://github.com/OttoYiu/django-cors-headers verarbeitet Preflight-Anforderungen für OPTIONS in der Middleware und gibt direkt eine Antwort zurück. Es spielt keine Rolle, was das REST-Framework hier tut, da die Anfrage/Antwort abgefangen wird, bevor sie überhaupt auf das REST-Framework trifft.

Mir ist nicht klar, dass wir hier ein Problem haben.

Danke. Wir verwenden keine django-cors-Header, aber vielleicht sollten wir es sein.
Ich stimme den früheren Kommentaren immer noch zu, dass DRF kein externes Paket benötigen sollte, um W3C-konforme CORS-Anfragen zu verarbeiten.

Ja, es gibt eine Reihe von Paketen von Drittanbietern, um das Problem zu umgehen.
Aber auch dieses Ticket wurde erstellt, weil DRF mit den W3C CORS-Spezifikationen nicht kompatibel ist und behoben werden sollte.

Heutzutage dient die überwiegende Mehrheit der OPTIONS Anfragen an Endpunkte Preflight-Zwecken, und es ist wahrscheinlich trügerisch für neue DRF-Benutzer, diese Probleme aufgrund von DRF-Meinungsentscheidungen manuell bearbeiten zu müssen.

Heutzutage dient die überwiegende Mehrheit der OPTIONS Anfragen an Endpunkte Preflight-Zwecken, und es ist wahrscheinlich trügerisch für neue DRF-Benutzer, diese Probleme aufgrund von DRF-Meinungsentscheidungen manuell bearbeiten zu müssen.

Nur weil CORS die OPTIONS-Methode gekapert hat, bedeutet dies nicht, dass ihre Sicherheit standardmäßig geändert werden sollte, um dies zu ermöglichen. Insbesondere wenn die CORS-Unterstützung richtig implementiert ist, funktioniert sie so, wie sie es soll. Entschuldigung, aber ich denke, Entwickler, die dies ändern möchten, müssen ihre eigenen Meinungen überprüfen und nicht die DRF-Betreuer.

Vielleicht eine Verbesserung der Dokumentation, um vorzuschlagen, dass, wenn CORS benötigt wird, ein Modul verwendet wird, das bei der richtigen Implementierung hilft, anstatt das Rad neu zu erfinden.

Lassen Sie uns das Level senken.

Middleware ist der sinnvolle Ort, um mit CORS-Headern umzugehen. Wir könnten das in das REST-Framework einbauen, aber das vorhandene Paket hat es bereits sehr gut abgedeckt.

Der Rumpf, den das REST-Framework zufällig für OPTIONS Anfragen zurückgibt, ist hier irrelevant, da vorgeprüfte CORS-Anfragen sowieso von Middleware abgefangen werden sollten und Standard-CORS-Anfragen von jeder HTTP-Methode sein können.

Ich würde vollkommen glücklich sein für REST Framework nicht serviert , diese Antwort Körper standardmäßig zu bewegen, aber das ist etwas anders als die Ausgabe von CORS - Unterstützung, und es ist nicht so sehr , dass REST Rahmen dort opinionated hat Verhalten, sondern dass es Verhalten, das älter ist als CORS weit verbreitet wurde.

Mir scheint, dass die Middleware-Lösung allein nicht ausreicht oder zumindest eine Kooperation von DRF erfordert. Wenn Sie sich den referenzierten Code ansehen, können Sie sehen, dass globale Standardwerte verwendet werden, um die CORS-Header bereitzustellen. Aber woher weiß ich global, welche Header jede Ansicht unterstützt?

Daher würde ich gerne eine Standardmethode für Middleware sehen, um sich über Viewsets/Views in diese Konfigurationsoptionen einzuklinken: eine Methode allowed_cors_headers zum Beispiel.

Ich stimme zu, dass dies nicht von Ansichten gehandhabt werden muss, aber es muss unbedingt das Wissen der Ansicht respektieren, was sie dienen kann.

Es kann sich auch lohnen, eine Überschreibung auf Router-Ebene zu haben, die auf alle Ansichten im Router angewendet wird.

Weitere Überlegungen:

  • je nach Herkunft unterschiedlich
  • je nach Inhaltstyp variieren

Dies ist nicht meine Lieblingswahl, aber der Trend geht zur Verwendung eines Drittanbieters.

Ich denke, wir können dieses Ticket schließen, sobald die Dokumentation mit den richtigen Informationen für CORS-Benutzer aktualisiert wurde (z. B.: "Achtung, DRF erfüllt die W3C CORS-Spezifikationen bei OPTIONS-Anforderungen nicht richtige Middleware, andernfalls können Ihre Preflight-Anfragen aufgrund fehlender Autorisierung fehlschlagen" usw.)

Die vorhandene Dokumentation https://www.django-rest-framework.org/topics/ajax-csrf-cors/#cors ist durchaus sinnvoll und der Umgang mit CORS in Middleware auf jeden Fall der richtige Ansatz.

Aber woher weiß ich global, welche Header jede Ansicht unterstützt?

Sie müssen nicht - Sie müssen die CORS-Richtlinie der Site kennen.

Gerne nehmen Sie einen Pull-Request an, um die CORS-Dokumente hervorzuheben oder an anderer Stelle einzufügen, die auch angemessen ist. Ansonsten gibt es hier kein umsetzbares Problem.

Mein Missverständnis der Spezifikation, oops.

In diesem Fall wäre die Unterstützung besser in den Django-Sites enthalten contrib
Paket, nicht drf.

Le mar. 19 fevr. 2019 10:22 Uhr, Tom Christie [email protected] a
schreiben:

Die vorhandene Dokumentation
https://www.django-rest-framework.org/topics/ajax-csrf-cors/#cors is
durchaus sinnvoll, und der Umgang mit CORS in Middleware ist richtig
auf jeden fall herangehen.

Aber woher weiß ich global, welche Header jede Ansicht unterstützt?

Sie müssen nicht - Sie müssen die CORS-Richtlinie der Site kennen.


Sie erhalten dies, weil Sie einen Kommentar abgegeben haben.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/encode/django-rest-framework/issues/5616#issuecomment-465146969 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/AFhtlM6bG-Bs2CvoO972pIfwvxLHbzAxks5vPAjAgaJpZM4Qlvkn
.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen