CORS
Frag, bevor du sendest
Das Frontend kann /about jetzt lesen. Wofür es da ist, ist die Buchliste, und GET /books braucht ein Token:
GET /books HTTP/1.1
Host: reading.example
Origin: https://app.reading.example
Authorization: Bearer <a token>
Noch keine Regel in deinem mini_browser.py sagt, dass diese Anfrage mehr braucht als das GET aus Lektion 2. Ein echter Browser behandelt die beiden unterschiedlich. Vor dieser hier sendet er eine andere Anfrage, und diese Lektion erklärt, welche und warum.
Was ein Formular schon immer senden konnte
Denk an das alte Web aus Kapitel 8, Lektion 4. Lange bevor Skripte Anfragen senden konnten, konnte eine Seite ein Formular enthalten, das an jede andere Site sendete. Ein solches Formular kann zwei application/x-www-form-urlencoded, multipart/form-data oder text/plain. Es kann keinen eigenen Header hinzufügen.
Server, die vor
Die Linie
Eine Anfrage an einen anderen
ihre Methode ist GET, HEAD oder POST;
jeder Header, den die Seite gesetzt hat, ist
Accept,Accept-Language,Content-LanguageoderContent-Type, und einContent-Typeist einer der drei Formular-.
Diese vier Namen heißen die Safelist. Ein Header außerhalb davon genügt, um die Linie zu überschreiten, und jede andere Methode genauso.
Der nächste Block zieht die Linie für sieben Anfragen. Lies asked_first, bevor du ihn ausführst, und sag die letzte Spalte voraus.
Warum wird nach GET /books mit einem Header Authorization zuerst gefragt, obwohl GET eine einfache Methode ist?
Welche dieser Anfragen an einen anderen Origin sendet ein Browser, ohne vorher zu fragen?
Der Standard ist feiner als dieser Block. Er begrenzt außerdem die Länge und die Zeichen des Range mit einem einfachen Wert zur Safelist, und er erlaubt einem Browser, sich eine Antwort eine Weile zu merken. Das Modell in diesem Kurs lässt alle drei weg.
Die Frage und die Antwort
Die Anfrage, die fragt, heißt Preflight. Sie ist eine Anfrage OPTIONS an dieselbe GET /books des Frontends, so wie dein Server ihn am Ende von Lektion 4 beantworten wird:
OPTIONS /books HTTP/1.1
Host: reading.example
Origin: https://app.reading.example
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
HTTP/1.1 204 No Content
access-control-allow-methods: GET, HEAD, POST, PUT, DELETE
access-control-allow-headers: authorization, content-type
vary: Origin
access-control-allow-origin: https://app.reading.example
Die Zeilen Server und Date der Antwort sind weggelassen. Die Frage nennt drei Dinge: den Origin der Seite, die Methode, die die echte Anfrage verwenden wird, und die Header außerhalb der Safelist, die sie tragen wird. Sie nennt die Header und sendet ihre Werte nicht.
Die Antwort sagt Ja mit drei eigenen Dingen. Ihr Status liegt zwischen 200 und 299. Access-Control-Allow-Origin nennt den Origin der Seite. Und die beiden Access-Control-Allow-Methods, außer sie ist GET, HEAD oder POST, die nicht aufgeführt werden müssen, und jeder erfragte Header steht in Access-Control-Allow-Headers.
the page the browser the server
1. asks for GET /books
with a token ---->
2. OPTIONS /books: may this origin send
GET with authorization?
--------------->
3. 204, the origin,
the two lists
<---------------
4. the answer fits: GET /books
with the token
--------------->
5. 200, the list,
the origin again
<---------------
<--------------- 6. hands the response to the script
Diesmal sind es sechs Schritte. Die Seite fragt. Der Browser sendet den Preflight. Der Server beantwortet ihn. Wenn die Antwort passt, sendet der Browser die echte Anfrage, und wenn sie nicht passt, hört der Browser auf, und die echte Anfrage geht nie hinaus. Der Server beantwortet die echte Anfrage noch einmal mit Access-Control-Allow-Origin, und der Browser wendet die Prüfung aus Lektion 2 auf diese Antwort an.
Ein Detail entscheidet, wie du den Server schreibst. Der Preflight trägt kein Authorization. Er fragt, ob eine Anfrage überhaupt gesendet werden darf, und der Server muss ihn beantworten können, ohne zu wissen, wer fragt.
Ein Server beantwortet OPTIONS /books mit 401, weil kein Token mitkam. Was passiert mit der echten Anfrage des Frontends?
Dein Server ist heute dieser Server. Seine Buch-Routen prüfen das Token vor allem anderen, also bekommt OPTIONS /books eine 401.
Warum das alte Server schützt und nicht deinen
Ein Preflight ist der Browser, der vorsichtig ist im Namen eines Servers, der nie von CORS gehört hat. Ein solcher Server beantwortet die Frage nicht, also kann eine Seite auf einem anderen Origin ihm nur senden, was ein Formular schon immer senden konnte.
Die Vorsicht endet an der Linie. Nach einer einfachen Anfrage wird nie gefragt, und genau das hat Kapitel 8 genutzt: Das gefälschte Löschen war ein Formular-POST. Ein Server kann eine seiner eigenen Routen auch versehentlich über die Linie schieben. Eine Route, die text/plain gekennzeichnet ist, nimmt eine Anfrage an, die jeder Browser sendet, ohne zu fragen. Dein Löschformular lehnt seit Kapitel 7 jeden Content-Type außer seinem eigenen ab, und die Buch-Routen nehmen JSON nur unter application/json an.