lars
webmobiledatenbankendevopsarchitektur
hello (at) larskoelpin.de

Der OAuth2 PKCE-Flow

September 11, 2022
algorithmswebarchitecture

OAuth ist das Standard-Protokoll für verteilte Anwendungen. Für Mobile- und Web-Applikationen hat sich der Proof Key for Code Exchange (PKCE)-Flow zum Standard etabliert. In diesem Beitrag schaue ich mir den PKCE-Flow für Mobile und Web-Anwendungen an.

Der PKCE Flow

Der Proof Key for Code Exchange (PKCE)-Flow ist wohl deshalb für Mobile und Web-Anwendungen interessant, weil dieser es nicht vorsieht Client Secrets vorzuhalten. Stattdessen wird ein temporärer Code generiert um die Applikation im Flow verifizieren zu können. Wie diese Verifikation funktioniert, ist der Kern des PKCE-Flows.

Zunächst ist es hilfreich zu verstehen, wie der PKCE-Flow funktioniert, um dann zu erkennen, welches Problem gelöst wurde. Dazu kann zunächst der konkrete Ablauf des Flows aus dem folgenden Bild entnommen werden.

Der PKCE-Flow
Der PKCE-Flow

(1) Der PKCE-Flow startet wie jeder OAuth-Flow. Ein Nutzer möchte über eine Mobile- oder einer Single-Page-Application, eine Resource anfragen.
Das könnte z.B. ein ID-Token sein, der seine Nutzerdaten enthält, die in einem anderen Service (z.B. Github) bereits gepflegt wurden. Dazu muss die Applikation mit einem Server, der die Rechte verwaltet – dem Authorization-Server – kommunizieren.

(2) (3) Die Applikation generiert dann zwei Dinge: Zum einen wird eine zufällige, kryptografisch sichere, Zahl generiert. Diese Zahl ist der code verifier. Im genauen Wortlaut heißt es im RFC:

 ... The client SHOULD create a "code_verifier" with a minimum of 256 bits
   of entropy.  This can be done by having a suitable random number
   generator create a 32-octet sequence.  ...

Zum Anderen wird diese Zahl mit einer Hash-Funktion (code_challenge_method, in der Regel SHA256) gehasht. Das Ergebnis dieser Operation ist die code_challenge.

Beispiel:

code_verifier = mySecretPassword
code_challenge_method = sha256
code_challenge = sha256(mySecretPassword) = 2250e74c6f823de9d70c2222802cd059dc970f56ed8d41d5d22d1a6d4a2ab66f

Diese drei Daten werden gemeinsam genutzt, um die Applikation zu verifizieren.

(4) Die Applikation fragt die vom Nutzer angefragten Rechte (“scopes”) beim Authorization-Server an. Dabei sendet der Client den ersten Teil der Daten: Die code_challenge sowie die code_challenge_method. Der Authorization-Server kennt also ab diesem Schritt einen Hash-Wert, dessen Funktion, nicht aber, aus welchem Wert dieser initial errechnet wurde.

Der Authorizaion-Server leitet den Nutzer dann auf eine Seite weiter, (5) (6) um die Scope-Berechtigung vom Nutzer einwilligen zu lassen.

(7) Ab hier beginnt der der spannende Teil des PKCE-Flow. Der Authorization-Server generiert einen Auth-Code, der gegen den Token mit den entsprechenden scopes eingetauscht werden kann.

Dieser wird in Schritt (8) zusammen mit dem Authorization-Code übertragen. Zu diesem Zeitpunkt wurde die code_challenge bereits übertragen. Der code_verifier ist das, was den Client letztlich verifiziert. Dadurch, dass die code_challenge der Hash-Wert des code_verifiers ist, kann die code_challenge aus dem code_verifier errechnet werden.

Das heißt also, dass der Authorization-Server im Schritt (9) den enthaltenden code_verifier mit dem angegebenen Algorithmus (SHA256) hashen, und letztlich mit dem aus Schritt (4) erhaltenden challenge vergleichen muss.

Gleicht das das Ergebnis der im Schritt (4) erhaltenen code_challenge ist die Applikation verifiziert.

Der genaue Wortlaut aus dem RFC lautet hierzu.

BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge

Der Authorization-Server tauscht den Authorization-Code und den code_verifier gegen ein Token aus. Der Authorization-Flow ist beendet.

Was wäre wenn

Doch welches Problem wurde konkret durch den PKCE-Flow und den Variablen (code_challenge, code_veifier, code_challenge_method) der Flow eigentlich? Es geht primär darum Attack-Vektoren zu reduzieren.

Speziell kann es passieren, dass es einem Angreifer irgendwie gelingt in Schritt (7) auf seine Applikation weiterzuleiten. 

Der PKCE mit einer schädlichen Software
Der PKCE mit einer schädlichen Software

Im Bild dargestellt durch den roten Bereich mit der Aufschrift “Maclicous”. Wäre es möglich den Code direkt einzutauschen, dann hätte die schädliche Applikation nun vollen Zugriff die API.

Ist es aber glücklicherweise nicht. Denn die Applikation braucht den code_verifier und wie zu sehen, ist dieser nur im Speicher der orignalen Applikation verfügbar. Die schädliche Applikation hat deshalb keine Möglichkeit die Berechtigung zum Erhalten des Token zu verifizieren und die API bleibt geschützt.

Zusammenfassung

Der PKCE ermöglicht es einen sicheren Flow für Mobile und Web-Applikationen bereitzustellen. Zwar gibt es diverse andere Flows (Implicit Flow, Authorization-Code-Flow), allerdings sind diese entweder nur im Web verfügbar, oder nicht darauf ausgelegt in öffentlichen Clients angewand zu werden.

Der PKCE-Flow verifiziert unseren Client durch ein Kombination aus code_challenge und code_verifier. Dadurch, dass der code_verifier nur im Speicher der Applikation lebt, ist es einer schädlichen Applikationen nicht möglich den Authorization-Code gegen einen validen Token einzutauschen.

Links

https://www.rfc-editor.org/rfc/rfc7636

https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-proof-key-for-code-exchange-pkce