HTTPS, Zertifikate und Sicherheits-Header
Header, die einem Browser Anweisungen geben
Die Verbindung ist in Ordnung: Der Client weiß, wer das andere Ende hält. Sieh dir jetzt an, was darüber ankommt. Die Seite deines Dienstes enthält Text, der aus Anfragen stammt, zum Beispiel Buchtitel. In Kapitel 7 hast du einen Aufruf von html.escape zwischen einen Titel und die Seite gesetzt. Wenn auch nur ein
Eine einzige Verteidigungslinie ist nicht viel für einen Fehler, der so leicht passiert. Eine zweite Linie muss im Browser leben, denn dort würde das Skript laufen. Der Server kann sie mit einem
Eine Policy ist eine Liste von Quellen
Der Header ist Content-Security-Policy. Sein Wert ist eine Policy: eine
content-security-policy:
default-src 'self'; form-action 'self'; frame-ancestors 'none'
Zerleg ihn so, wie es ein Browser tut. Führ das aus:
Die erste Zeile ist default-src allows ["'self'"]. default-src ist die Direktive für alles, was eine Seite lädt: Skripte, Bilder, Stylesheets. 'self', mit seinen Anführungszeichen, bedeutet die eigene default-src 'self' sagt also, dass diese Seite Dinge von ihrer eigenen Origin laden darf und von nirgendwo sonst.
Eine Policy ändert nichts auf dem Server und nichts an der Verbindung. Der Server sendet die Seite so, wie sie ist. Der Header ist eine Anweisung, und nur ein Browser führt sie aus.
wire.py ruft die Seite und ihren Policy-Header ab. Was ändert die Policy?
Was default-src ‘self’ verhindert
Angenommen, das Escapen wird einmal übersehen. Das sind die zwei Fassungen eines Titels in seinem <span class="title"> auf der Seite, escapt und nicht escapt:
<script src="https://other.example/steal.js"></script>
<script src="https://other.example/steal.js"></script>
Die zweite Zeile ist ein Script-Element. Unter default-src 'self' weigert sich ein Browser, der dem Standard folgt, es zu laden, weil https://other.example nicht die Origin der Seite ist.
Ein Angreifer braucht allerdings keine andere Site. Das Skript kann in der Seite selbst stehen, zwischen den Tags, oder in einem Attribut wie onclick. Das sind Inline-Skripte. Ein Inline-Skript wird aus keiner Datei geladen, also deckt 'self' es nicht ab. Eine Policy erlaubt Inline-Skripte nur, wenn sie es sagt, mit der Quelle 'unsafe-inline'. Der Name ist eine Warnung. Deine Policy enthält sie nicht, also führt ein Browser auf deiner Seite kein Inline-Skript und keinen Inline-Event-Handler aus.
Hier ist ein Modell dieser Entscheidung. Es deckt zwei Regeln eines langen Standards ab: woher eine Skriptdatei kommt und ob ein Skript inline ist. Führ es aus:
Die ersten beiden Zeilen sind https://reading.example/app.js True und https://other.example/steal.js False. Diese Policy funktioniert für deine Seite, weil page.html kein eigenes Skript und keine eigenen Styles hat. Eine größere Seite, die Inline-Skripte braucht, listet jedes davon in ihrer Policy mit einer Nonce oder einem Hash auf, und eine neue Policy kann zuerst im Report-only-Modus ausprobiert werden. Das sind Namen zum Wiedererkennen.
Ein übersehenes html.escape lässt <script>alert(1)</script> in die Seite, mit dem Code zwischen den Tags. Die Antwort sagt default-src 'self'. Was macht ein Browser damit?
Was gibt die letzte Zeile des zweiten Blocks aus?
Was sie nicht abdeckt
default-src ist ein Rückfallwert für die Direktiven über das Laden. Zwei der Dinge, die eine Seite tun kann, sind kein Laden, und für sie gilt dieser Rückfall nicht.
Das erste ist, wohin ein Formular auf der Seite seine Felder senden darf. Eingeschleustes Markup könnte das Ziel des Formulars auf eine andere Site ändern. form-action 'self' hält jedes Formular der Seite auf ihre eigene Origin gerichtet.
Das zweite ist, wer die Seite in einem Frame anzeigen darf. Eine andere Site kann deine Seite in einen unsichtbaren Frame setzen und einen Leser dazu bringen, darauf zu klicken, was Clickjacking heißt. frame-ancestors 'none' sagt dem Browser, dass keine Seite diese einrahmen darf. Ein älterer Header, X-Frame-Options, verlangt dasselbe.
Was davon lässt default-src 'self' offen?
Ein zweites Schloss, kein Ersatz
Die Policy macht das Escapen nicht überflüssig. Ohne html.escape kann ein Titel immer noch Markup hinzufügen: eine Überschrift, einen Link, ein Formular, das nach einem Passwort fragt. Kein Skript läuft, und die Seite zeigt trotzdem, was ein Angreifer geschrieben hat. Das Escapen hält die Seite korrekt. Die Policy begrenzt den Schaden, wenn das Escapen einmal übersehen wird. Und die Policy hilft nur in einem Browser, der sie anwendet.
Ist html.escape in render_page() mit dieser Policy noch nötig?
Du kannst die Policy jetzt lesen. Noch trägt keine Antwort deines Dienstes sie. Die nächste Lektion fügt sie hinzu, mit drei weiteren Headern, in einer