In seinem Artikel „Using DPoP to Prevent Session Theft“ beschäftigt sich der Autor mit einer Frage, die viele Webentwickler umtreibt: Was passiert, wenn ein Angreifer das Session-Token eines Nutzers stiehlt? Das ist kein hypothetisches Szenario – gestohlene Session-Tokens gehören zu den gängigsten Angriffsmethoden. Der Autor zeigt, wie eine OAuth-Erweiterung namens DPoP dieses Problem entschärfen kann.
Stell dir vor, du gibst jemandem eine Kopie deines Haustürschlüssels. Solange der Schlüssel funktioniert, kann der Eindringling kommen und gehen, wann er will. Du könntest das Schloss austauschen, aber das hilft erst, nachdem du den Diebstahl bemerkt hast. Ähnlich sieht es bei Session-Tokens aus: Wer den Token besitzt, kann sich als der eingeloggte Nutzer ausgeben – und zwar so lange, bis der Token abläuft oder invalidiert wird. Genau hier setzt DPoP an.
Die Grundidee: Kein Token allein, sondern ein Schlüsselbeweis
Der Kern von DPoP ist erstaunlich einfach. Statt sich nur auf den Session-Token zu verlassen, wird ein Schlüsselpaar aus öffentlichem und privatem Schlüssel erzeugt. Der öffentliche Schlüssel wird bei der Anmeldung an die Session gebunden. Bei jeder Anfrage muss der Client eine Signatur mitschicken, die mit dem privaten Schlüssel erzeugt wurde. Der Server prüft dann nicht nur den Session-Token, sondern auch, ob die Signatur zu dem hinterlegten öffentlichen Schlüssel passt.
Warum das funktioniert? Der private Schlüssel ist das entscheidende Geheimnis. Wer nur den Token stiehlt, hat noch keinen Zugriff, denn die Signatur fehlt. Ein Angreifer müsste also sowohl den Token als auch den privaten Schlüssel in die Hand bekommen. Genau das macht den Angriff deutlich schwerer.
Eine naheliegende Frage: Warum stiehlt der Angreifer nicht einfach den privaten Schlüssel mit? Der Autor verweist auf einen entscheidenden Unterschied: Private Schlüssel können in einer Umgebung gespeichert werden, die sie vor unerlaubtem Export schützt. Die WebCrypto-API in Browsern bietet zum Beispiel die Möglichkeit, Schlüssel als „nicht extrahierbar“ zu erzeugen. Das bedeutet, dass der Code im Browser zwar den Schlüssel zum Signieren nutzen kann, aber nicht an den eigentlichen Wert des Schlüssels herankommt. Eine bösartige Chrome-Erweiterung kann zwar Cookies, LocalStorage oder Speicherinhalte auslesen, aber nicht den privaten Schlüssel selbst. Sie könnte den Schlüssel höchstens benutzen, solange die Erweiterung aktiv ist. Nachdem sie entfernt wurde, sind alle von ihr signierten Daten wertlos – ein großer Unterschied zu gestohlenen Tokens, die oft noch tagelang gültig bleiben.
Was genau wird signiert?
Damit der Server die Signatur sinnvoll prüfen kann, muss klar sein, welche Daten signiert werden. Der Autor erläutert ein JWT-basiertes Format mit mehreren Feldern, die jeweils eine bestimmte Sicherheitsanforderung abdecken.
iat ist der Zeitstempel der Signatur. Er begrenzt die Gültigkeitsdauer der Signatur, weil der Server nur Anfragen akzeptiert, die innerhalb eines kurzen Zeitfensters erstellt wurden. jti ist eine zufällig generierte, eindeutige ID pro Anfrage. Der Server merkt sich alle kürzlich verwendeten jti-Werte und lehnt Wiederholungen ab – so werden Replay-Angriffe verhindert. htm und htu sind die HTTP-Methode und die URL. Damit ist die Signatur fest an eine bestimmte Anfrage gebunden. Eine abgefangene Signatur lässt sich nicht auf einen anderen Endpunkt oder eine andere Methode übertragen.
Besonders wichtig ist ath – der Hash des Session-Tokens. Der Autor weist darauf hin, dass dieses Feld in der OAuth-Welt eigentlich verpflichtend ist, sobald ein Tokens existiert. Er selbst vereinfachte das etwas: In manchen Browser-Szenarien ist es nicht trivial, an den Session-Token heranzukommen, etwa wenn ein HttpOnly-Cookie verwendet wird. Dann kann man auf das Feld verzichten, auch wenn das die Sicherheit etwas reduziert.
Und dann gibt es noch das nonce – eine zufällige Zeichenkette, die der Server vorgibt. Sie ist technisch optional, aber aus Sicht des Autors eines der mächtigsten Werkzeuge in DPoP. Stellen wir uns vor, ein Angreifer hat sich per XSS in die Seite eingeschlichen und kann mit dem privaten Schlüssel signieren. Ohne nonce kann er sich signierte Payloads für die Zukunft zurechtlegen. Mit nonce kann der Server jedoch plötzlich neue Nonces ausgeben und damit alle vorab generierten Signaturen wertlos machen. Der Server hat also eine Art Fernbedienung, um Anfragen aus der Vergangenheit zu invalidieren.
Die stillen Stärken von DPoP
Die Kombination dieser Felder ergibt eine starke Verkettung: Jede Signatur ist kurzlebig, an einen konkreten Request gebunden, gegen Wiederverwendung geschützt und an einen nicht exportierbaren Schlüssel gekoppelt. Der Autor nennt noch einen weiteren Vorteil: DPoP löst elegant das Problem mit wechselnden IP-Adressen. Viele Session-Management-Richtlinien versuchen, Sessions an die IP-Adresse zu binden. Aber wechselt die IP, weil der Nutzer ins Café zieht oder unterwegs ist, kann das zu Fehlern führen. Der Autor schlägt deshalb vor, die nonce an IP-Adressen oder andere Risikofaktoren zu koppeln.
Die Idee dahinter: Kommt eine Anfrage von einer neuen IP, schickt der Server eine Fehlermeldung mit einer neuen nonce zurück. Diese nonce ist an die neue IP gebunden. Der Client – also das JavaScript im Browser – erzeugt im Hintergrund eine neue Signatur und schickt die Anfrage erneut. Die Anfrage funktioniert, vorausgesetzt, der Client besitzt den privaten Schlüssel. Für den Nutzer ist dieser Austausch unsichtbar. So entsteht ein leichter, stiller Challenge-Mechanismus, den der Server so oft auslösen kann, wie er will – ohne den Nutzer zu stören. Das ist deutlich praktischer als eine Zwei-Faktor-Abfrage.
Wo die Methode an ihre Grenzen stößt
DPoP ist kein Allheilmittel. Der Autor benennt zwei wesentliche Einschränkungen. Erstens kann DPoP nicht vor Cross-Site-Scripting schützen. Wenn ein Angreifer Code in der Seite ausführen kann, hat er auch Zugriff auf den Signiermechanismus des privaten Schlüssels. Er kann dann selbst signierte Anfragen absetzen. Allerdings hilft DPoP nach der Beseitigung der XSS-Schwachstelle: Da neue nonces ausgegeben werden, sind alle vom Angreifer gesammelten Daten wertlos. XSS muss also trotzdem separat bekämpft werden – die OWASP-Cheat-Sheets geben dafür hilfreiche Empfehlungen.
Zweitens setzt DPoP JavaScript voraus. Bei Server-Side-Rendering werden Daten oft geladen, bevor JavaScript ausgeführt wird. Diese initialen Datenabrufe kann man nicht mit DPoP schützen. Der Autor merkt aber an, dass man nicht jede Anfrage absichern muss. Für kritische Mutationen, die im Browser per fetch laufen, funktioniert DPoP problemlos. Man muss also abwägen, welche Requests den zusätzlichen Schutz wirklich brauchen.
Fazit: Ein zusätzliches Werkzeug, kein Wundermittel
Der Autor bleibt am Ende geerdet: DPoP ist eine sinnvolle Erweiterung für alle, die ihre Sessions besser gegen Diebstahl absichern wollen. Es bindet das Session-Token an einen privaten Schlüssel, der im Browser sicher aufbewahrt wird. Damit wird ein gestohlener Token allein nutzlos. Für Angreifer wird es deutlich aufwendiger, beide Geheimnisse zu stehlen. Wer DPoP umsetzt, sollte die Details der OAuth-Spezifikation kennen – der Artikel vereinfacht bewusst einige Punkte, insbesondere im Umgang mit dem ath-Feld und beim Zugriff auf HttpOnly-Cookies. In der Praxis gilt: DPoP ersetzt keine andere Sicherheitsmaßnahme, aber es ergänzt sie gut. Und genau diese zusätzliche Schutzschicht kann im Ernstfall den Unterschied machen.
Quelle: byo.propelauth.com
