17. September 2019
6 min

OpenID Connect — Implicit Grant für meine Anwendung?

Jeder, der bereits Single-Page-Applications entwickelt hat und dort mit Authentifizierung zu tun hatte, kennt wahrscheinlich den OAuth 2.0/OpenID Connect Implicit Grant. Ist dieser einstige Standard denn heute noch empfehlenswert? Diese Frage kläre ich mit diesem Blog Post.
bildhübsche fotografie | Andreas Körner | www.a-koerner.de | info@a-koerner.de | +49 711 22 11 20

Für jeden, der noch nicht tiefer mit der Mechanik von OAuth 2.0 (Delegated Authorization) bzw. OpenID Connect (Authentication) vertraut ist, nochmal ein schneller Einstieg.

Authentication, zu deutsch Authentifizierung, ist der Nachweis und die Prüfung der Identität eines Nutzers. OpenID Connect ist hierfür das passende Protokoll.

Authorization, zu deutsch Autorisierung, beschäftigt sich mit den Berechtigungen eines Benutzers. Also mit der Entscheidung, ob ein Nutzer etwas Bestimmtes darf. Mit einem Spezialfall davon beschäftigt sich OAuth 2.0, nämlich mit der sog. delegated Authorization. Das ist die Berechtigung eines Benutzers im Namen eines anderen Benutzers, Resource Owner genannt, zu agieren.

Grundlegend spezifiziert OAuth 2.0, auf diesem Protokoll basiert übrigens OpenID Connect, vier Grant Types (gelegentlich auch Flows genannt): Authorization Code Grant, Implicit Grant, Client Credentials Grant und Resource Owner Credentials Grant.

Jeder Grant Type hat einen Anwendungsfall für den er sich besonders gut eignet:
(Spoiler-Alarm: Einer der Grant Types kann, durch eine Erweiterung, auch für den Anwendungsfall des Implicit Grants genutzt werden. Dazu später mehr.)

  • Authorization Code Grant: Klassische Web Anwendungen, die serverseitig, ohne dass der Benutzer es mitlesen kann, eine Verbindung zum Authorization Server (kurz AS) aufbauen können.
  • Implicit Grant: Single-Page-Applications, wo keine sichere (nicht abhörbare) Verbindung zwischen Anwendung und AS möglich ist.
  • Client Credentials Grant: Maschine-zu-Maschine Kommunikation, bei der kein Benutzer involviert ist. (Beispiel: Services identifizieren sich gegenüber einander mit einem Token, das mittels Client Credentials Grant bezogen wurde)
  • Resource Owner Credentials Grant: Sollte möglichst gar nicht verwendet werden. Hierbei wird Username und Passwort des Endanwenders gegen Tokens ausgetauscht.

Der Fokus dieses Artikels liegt, wie der Teaser bereits erahnen lässt, klar auf Single-Page-Applications. Dort kann der Authorization Code Grant nicht verwendet werden, da die Verbindung zwischen dem Browser des Users und dem Authorization Servers aufgebaut werden würde und diese nicht als „secure back-channel“ bezeichnet werden kann. Schließlich findet die Kommunikation sozusagen „vor den Augen des Users“ statt.

Eine Besonderheit hat der Authorization Code Grant bislang eigentlich fast immer gehabt (man kann davon abweichen, sollte es aber nicht) — es handelt sich bei dem Client um einen sogenannten „confidential Client“. Das bedeutet, er muss sich gegenüber dem Authorization Server mit eigenen Zugangsdaten, den Client Credentials (Client ID + Client Secret), ausweisen um einen Authorization Code gegen einen Access (und ID) Token auszutauschen. Wenn der Kanal problemlos abhörbar wäre, könnte man ohne weiteres diese Prüfung auch entfallen lassen, womit der Authorization Code Grant nicht im Geringsten sicherer als der Implicit Grant wäre.

Die Problematik um den Implicit Grant

Damit komme ich bereits zu einem Kernpunkt das Artikels: Warum sollte der Implicit Grant mittlerweile vermieden werden? Für OAuth 2.0 gibt es ein „OAuth 2.0 Security Best Current Practice“ Dokument. Die darin erwähnten Schwachstellen und Lösungsvorschläge sind analog auch auf OpenID Connect anwendbar, da es auf OAuth 2.0 basiert.

Folgende Schwachstellen sind auf den Implicit Grant anwendbar:

  • Unzureichende Prüfung der Redirect URI
    Diese Schwachstelle führt dazu, dass ein Angreifer die Redirect URI modifizieren kann. Nach der Modifikation werden die Tokens an die falsche Anwendung übertragen, nämlich die des Angreifers. Die daraus resultierenden Folgen sind denke ich offensichtlich.
  • Token „durchsickern“ durch Referrer Header
    Wird beispielsweise ein Bild eines Angreifers auf der Redirect URI geladen oder wird der Anwender anschließend auf die Seite eines Angreifers weitergeleitet, stehen die Tokens im Referrer Header.
  • Token „durchsickern“ aus der Browser History
    In der Browser Historie des Anwenders stehen die erhaltenen Tokens, sofern sie in den Query Parametern übertragen wurden.

Beim Authorization Code Grant versucht ein Angreifer entsprechend den Authorization Code zu stehlen und gegen Tokens auszutauschen. Beim klassischen Authorization Code Flow würde die Kommunikation zwischen Anwendung und Authorization Server auf einem sicheren Kanal stattfinden. Also so, dass der User, und damit auch potentielle Angreifer, dies gar nicht sehen. Das kann dadurch gewährleistet werden, dass direkt zwischen einem Application Server und dem Authorization Server kommuniziert wird.

Damit kein Unbefugter den Code-Austausch durchführen kann, sind Client Credentials der Anwendung, also technische Zugangsdaten für die Anwendung, erforderlich. Logischerweise sollten diese nur auf einem sicheren Kanal übertragen werden, was bei einer SPA nicht möglich ist.

Der Implicit Grant hat zudem historische Wurzeln. Vor der Einführung von Cross-Origin Resource Sharing (kurz CORS), war es für Web Anwendungen im Browser nur möglich, HTTP Requests (AJAX Requests) auf dem selben Host auszuführen. (Stichwort: Same Origin Policy) Alleine aus diesem Grund war der Authorization Code Grant, bei dem eine Anfrage an einen anderen Host (den AS) gestellt wird, nicht möglich.

Authorization Code Grant to the rescue

„Aber, aber… bis eben hieß es doch der wäre hierfür nicht anwendbar?“ — Genau diese Frage werden sich nun einige stellen, und ich muss sagen, die habe ich mir anfangs auch gestellt. Durch eine Erweiterung kann der Authorization Code Grant nun eben doch verwendet werden und räumt so mit einigen Schwachstellen des Implicit Grant auf. Die Erweiterung wird kurz als PKCE (gesprochen Pixy) bezeichnet. Das Akronym bedeutet ausgeschrieben Proof Key for Code Exchange. Es ist also, frei übersetzt, ein Berechtigungsmerkmal für den (Authorization) Code Austausch. So viel zur trockenen Theorie.

OAuth 2.0 / OpenID Connect Authorization Code Flow mit PKCE

Die Grafik zeigt den Ablauf des Authorization Code Grants mit PKCE. Der Authorization Code Grant (ohne PKCE) sollte verstanden sein, denn wichtige Merkmale, wie beispielsweise die Redirect URI wurden der Übersicht halber weggelassen. Nun aber zum Ablauf:

  1. Nachdem der Anwender auf „Login“ im Frontend geklickt hat, wird er auf den Authorization-Endpunkt des AS weitergeleitet. Hierbei wird insbesondere die code_challenge übertragen.
    Die
    code_challenge ist eine SHA-256 gehashte Zeichenkette, die kryptografisch zufällig erstellt sein sollte. Sie sollte also nicht „erraten“ werden können. Außerdem muss sie mindestens 43 und maximal 128 Zeichen lang sein. Die ursprüngliche Zeichenkette, vor dem Hashing, bezeichnet man als code_verifier.
  2. Nach erfolgreichem Login erzeugt der AS einen authorization_code. Zusammen mit der erhaltenen code_challenge wird er für spätere Zwecke auf dem AS abgelegt. Außerdem wird der Benutzer auf die Anwendung zurückgeleitet. Hierbei wird ebenfalls der authorization_code mitgeschickt.
  3. Die Anwendung setzt anschließend einen Request, üblicherweise in Form eines XHR (= XMLHttpRequest; oft einfach als AJAX Request bezeichnet), an den Token-Endpunkt des AS ab. Dabei wird der authorization_code und der code_verifier übertragen.
  4. Der AS hashed nun den code_verifier mit dem SHA-256 Verfahren. Dieser Hash, in der Grafik als h_code_verifier bezeichnet, wird nun mit der gespeicherten code_challenge aus Schritt (1) verglichen.
  5. Falls die Hashes übereinstimmen, wird ein Access Token und ID Token an die Anwendung übertragen.

SHA-256 ist übrigens das zwingend geforderte Verfahren, dass jeder AS implementieren muss, sofern der Authorization Code Flow mit PKCE unterstützt wird. Falls der Client dieses Verfahren technisch nicht unterstützen kann, könnte er die Code Challenge auch im Plain-Format übertragen. Das sollte aus Sicherheitsgründen unbedingt vermieden werden. Speziell wenn der Server vorgibt SHA-256 nicht zu unterstützen, sollten Clients in keinem Fall auf das Format Plain zurückfallen.

Fazit

Womit räumt dieser Flow denn nun auf? Dazu nehme ich die Liste von oben zur Hand:

  • Unzureichende Prüfung der Redirect URI
    Grundlegend sollte auch hier auf eine ausreichende Prüfung der Redirect URI geachtet werden. Zusätzlich sollte nach einem Versuch des Austauschs (Code -> Tokens) der Code für weitere Versuche gesperrt werden. Eine entsprechende Info im Log und ans Monitoring können Aufschluss über versuchte Angriffe geben.
    Neben der einmaligen Verwendbarkeit sollte er zudem eine sehr kurze Lebensdauer (wenige Minuten) besitzen. Standardkonfigurationen von gängigen Authorization Servern (z. B. Keycloak) machen das bereits.
    Da der Angreifer aber nicht den
    code_verifier kennt, kann er ihn auch nicht dem AS vorweisen. Bei ausreichend hoher Entropie ist ein erraten „unmöglich“.
  • Token „durchsickern“ durch Referrer Header
    Dieser Angriff entfällt. Das Token wird über eine separate Verbindung übertragen.
  • Token „durchsickern“ aus der Browser History
    Dieser Angriff entfällt. Das Token wird über eine separate Verbindung übertragen.
  • Kein sicherer Kanal möglich, daher keine Verifizierung des „Einlösers“ des Authorization Codes
    Dieser Angriff entfällt. Statt den Client Credentials liefert die Anwendung zu Beginn des Flows einen Hash und beim „Einlösen“ des Authorization Codes den Ursprungswert, der nur der Anwendung bekannt sein kann. (Stichwort: Hohe Entropie!)

Wie man daran ganz gut erkennen kann, behebt der Authorization Grant mit PKCE einige Probleme des Implicit Grant und sollte daher immer statt diesem verwendet werden. Fairerweise muss ich einräumen, dass viele Bibliotheken für OAuth 2.0 / OpenID Connect die oben genannten Problemen bereits für den Implicit Grant gelöst haben. Das bedeutet natürlich aber nicht, dass man sich darauf bei neuen Anwendungen verlassen sollte, wenn es eine bessere Lösung ohne größere Nachteile gibt. Speziell wenn diese Lösung im Rahmen einer Best-Practice empfohlen wird!

Bei Fragen rund um OpenID Connect oder OAuth 2.0 stehen Ihnen meine Kollegen und ich gerne zur Verfügung.