HTTP Authentication
Der Schutz von Web-Ressourcen und Daten ist ein entscheidender Aspekt bei der Entwicklung von Web-Anwendungen. Daten sollten nicht jedem im Internet zugänglich sein, daher ist es wichtig, einen Mechanismus zu haben, der diese Daten schützt. Das Hypertext Transfer Protocol unterstützt eine Reihe von Methoden zur Zugriffskontrolle und Authentifizierung. Die HTTP-Authentifizierung wird verwendet, um eine Client-Anfrage an einen Server zu authentifizieren, indem er gezwungen wird, eine Art Nachweis mithilfe von Anmeldeinformationen zu erbringen, um auf Ressourcen auf diesem Server zugreifen zu können.
Im Folgenden wollen wir uns ansehen, welche verschiedenen Verfahren für die Authentifizierung eines Clients gegenüber einem Server über HTTP existieren.
HTTP authentication schemes
Im Allgemeinen erfolgt die Authentifizierung und Autorisierung über einen Autorisierungs-Header in der Anfrage:
Authorization: <type> <credentials>
Die Art und die Berechtigungsnachweise sind je nach Regelung unterschiedlich, die häufigsten sind die folgenden:
Basic
Dies ist die einfachste und, wie der Name schon sagt, die grundlegendste Variante. Die Authentifizierung erfolgt durch Angabe des Passworts für jede Client-Anfrage. Die Anmeldedaten werden als Base64-kodierte Paare aus User und Kennwort übertragen. Der Autorisierungs-Header sieht wie folgt aus
Authorization: Basic Base64(user:password)
Dabei steht Base64(user:password) für die Base64-kodierte Zeichenkette user:password.
Da Base64 keine Verschlüsselung bietet, wird das Kennwort im Klartext übertragen, und dieses Verfahren muss zusammen mit TLS verwendet werden.
Die technische Dokumentation finden Sie unter RFC 7617.
Digest
Im Gegensatz zur Basic-Authentifizierung muss bei der Digest-Authentifizierung das Kennwort nicht im Klartext übertragen werden. Stattdessen wird clientseitig aus Usernamen und Passwort ein Hash-Wert berechnet und an den Server gesendet.
Die Berechnung des Hashwerts ist recht komplex. Im Kern verwendet er eine Verkettung von Benutzername, Passwort und einem vom Server bereitgestellten Nonce-Wert und wendet darauf einen Hash-Algorithmus wie z. B. SHA-256 an.
Der Nonce-Wert ist obligatorisch, da er Wiederholungsangriffe verhindert, bei denen derselbe Hash mehrfach zur Authentifizierung eines Kunden verwendet wird, weshalb bei jeder nachfolgenden Anfrage ein neuer Nonce-Wert an den Kunden gesendet werden muss.
Es ist wichtig zu erwähnen, dass diese Authentifizierungsmethode als unsicher angesehen wird, da das Passwort im Klartext auf der Serverseite gespeichert werden muss, um die Hashes zu berechnen.Die technische Dokumentation finden Sie unter RFC 7616
Bearer
Bei der Bearer-Authentifizierung handelt es sich um eine Token-basierte Authentifizierung, bei der die Benutzer ihre Anmeldedaten einmal eingeben und im Gegenzug eine eindeutige verschlüsselte Zeichenfolge aus Zufallszeichen erhalten. Mit dem Token können Sie dann auf geschützte Systeme zugreifen, ohne Ihre Anmeldedaten erneut eingeben zu müssen. Das Token beweist, dass Sie bereits eine Zugriffsberechtigung haben. Der Autorisierungs-Header sieht wie folgt aus:
Authorization: Bearer <token>
Da das Token im Klartext übertragen wird, muss dieses Verfahren zusammen mit TLS verwendet werden.
Es ist erwähnenswert, dass Zugangstoken nicht unbedingt über einen Request-Header übertragen werden müssen, sondern auch über einen Body oder einen Query-Parameter.
Die technische Dokumentation finden Sie unter RFC 6749.
OAuth
Lassen Sie uns nun näher auf die Verwendung von tokenbasierten Authentifizierungsverfahren mit Bearer-Tokens eingehen. Wie oben beschrieben, wird ein Token verwendet, um Nutzer zu authentifizieren. Im Gegensatz zur Basic Authentifizierung, wo ein einfaches Passwort verwendet wird, ist aber noch nicht klar, wie man dieses Token erhält.
Es gibt verschiedene Arten von Protokollen, die auf HTTP aufbauen und eine tokenbasierte Authentifizierung verwenden. Eines der am weitesten verbreiteten ist OAuth. OAuth ermöglicht es Clients, auf geschützte Ressourcen zuzugreifen, indem sie einen Access-Token in Form einer Zeichenkette erhalten, anstatt die Benutzeranmeldeinformationen direkt zu verwenden.
Token werden von einem Autorisierungsserver mit Genehmigung des Resource-Owners an Clients ausgegeben. Der Client verwendet das Access-Token, um auf die geschützten Ressourcen zuzugreifen, die von dem Ressourcen-Server gehostet werden.
Im Allgemeinen muss ein Client, bevor er auf eine geschützte Ressource zugreifen kann, zunächst einen Authorization-Grant vom Resource-Owner einholen und diese Berechtigung dann gegen ein Access-Token austauschen. Das Access-Token repräsentiert den Geltungsbereich, die Dauer und andere Attribute, die durch den Authorization-Grant gewährt werden. In einigen Fällen kann ein Client seine eigenen Anmeldeinformationen direkt an einen Autorisierungsserver übermitteln, um ein Access-Token zu erhalten, ohne zuvor eine Authorization-Grant vom Eigentümer der Ressource einholen zu müssen.
Die nachfolgende Illustration bildet diesen Ablauf ab.
Die technische Dokumentation finden Sie unter RFC 6749.
Beispiel
Angenommen, wir verwenden eine Webanwendung zur Bearbeitung einiger Dateien und möchten diese Dateien in unserer Dropbox speichern. Außerdem möchten wir unsere Webanwendung zur Bearbeitung von Dateien authentifizieren und ihr mit unseren Google Mail-Anmeldedaten Zugriff auf unsere Dropbox-API geben.
In diesem Szenario haben wir:
Client = editing web application
Authorization grant server = web-browser (e.g chrome)
Authorization server = google mail
Resource server = Dropbox
- Der Client fordert die Autorisierung über den Webbrowser an, indem er einen Redirect-URI, eine Client-ID und einen Anfragebereich sendet.
- Der Webbrowser verwendet die Redirect-URI, um Client-Identifier und Scope an den Autorisierungsserver zu senden.
- Der Resource-Owner, der die Datei bearbeitet, muss die Google-Mail Anmeldedaten angeben, um sich beim Autorisierungsserver zu authentifizieren.
- Der Autorisierungsserver sendet eine Authorization-Grant an den Webbrowser zurück.
- Die Authorization-Grant wird an den Client weitergeleitet.
- Der Client sendet die Authorization-Grant an den Autorisierungsserver.
- Der Autorisierungsserver sendet einen Access-Token an den Client in der Annahme, dass der Authorization-Grant gültig ist.
- Der Client sendet das Access-Token an den Ressourcenserver.
- Der Ressourcenserver sendet die Ressource an den Client in der Annahme, dass das Token gültig ist.
Beachte:
- Es gibt verschiedene Grant-Typen. Je nach Anwendungsfall wird ein bestimmter Grant-Typ für die Autorisierung verwendet. Siehe grant-types.
- Am Ende eines OAuth-Autorisierungsablaufs muss der Ressourcenserver das vom Autorisierungsserver vergebene Access-Token validieren. Je nach Art des Access-Tokens kann es verschiedene Bewertungsmethoden geben.
- Bei allen Anfragen handelt es sich in der Regel um HTTP-Anfragen, sodass die Autorisierung im Allgemeinen über den oben beschriebenen HTTP-Header erfolgt.
Access-Token
Ein OAuth Access-Token ist eine Zeichenkette, die der OAuth-Client verwendet, um Anfragen an den Ressourcenserver zu stellen. Access-Token müssen nicht in einem bestimmten Format vorliegen, und in der Praxis haben verschiedene OAuth-Server viele verschiedene Formate für ihre Access-Token gewählt.
Es gibt eine Reihe von Eigenschaften von Access-Token, die für das Sicherheitsmodell von OAuth von grundlegender Bedeutung sind:
- Access-Token dürfen nicht vom OAuth-Client gelesen oder interpretiert werden. Der OAuth-Client ist nicht der Adressat des Tokens.
- Access-Token übermitteln dem OAuth-Client weder die Benutzeridentität noch andere Informationen über den Benutzer.
- Access-Token sollten nur für Anfragen an den Ressourcenserver verwendet werden. Außerdem dürfen ID-Token nicht für Anfragen an den Ressourcenserver verwendet werden.
In den meisten Fällen handelt es sich bei dem Access-Token um einen Bearer-Token. Es gibt hauptsächlich zwei verschiedene Arten von Bearer-Token. Je nach Art des Access-Tokens wertet der Ressourcenserver das Token unterschiedlich aus.
Identifier-based
Das Token stellt eine schwer zu erratende Zeichenfolge dar, die ein Schlüssel zu einem Datensatz in der Datenbank des Autorisierungsservers ist. Ein Ressourcenserver validiert ein solches Token durch einen Aufruf an den Introspection-Endpoint des Autorisierungsservers.
Self-contained
Das Token kodiert die gesamte Autorisierung in sich selbst und ist kryptografisch gegen Manipulationen geschützt. JSON Web Token (JWT) hat sich zum De-facto-Standard für in sich geschlossene Token entwickelt.
Beachte: JWT-Token können sowohl als Access-Token als auch als ID-Token verwendet werden.
OpenID Connect (OIDC)
OAuth wurde ursprünglich für die Autorisierung und nicht für die Authentifizierung entwickelt. Daher ist es für den Client nicht möglich, beim Zugriff auf geschützte Ressourcen Benutzerinformationen des Resource-Owners zu erhalten.
OpenID Connect wurde entwickelt, um dieses Problem zu lösen, indem diese Authentifizierung-Layer zusätzlich zu OAuth bereitgestellt wird.
Der Autorisierungsverlauf ist mehr oder weniger derselbe wie im obigen OAuth-Fall, bei dem ein Client auf die geschützte Ressource zugreift, indem er einen Autorisierungsserver verwendet, der ihm den Zugriff über ein Zugriffstoken gewährt. Der Hauptunterschied besteht jedoch darin, dass der Autorisierungsserver dem Client mit einem ID-Token in Form eines JWT-Tokens zusätzliche Informationen, wie zum Beispiel Benutzerinformationen liefert. Siehe OpenID Intro.
Die technische Dokumentation finden Sie unter OpenID.
JWT
JSON Web Token (JWT) ist ein offener Standard, der eine kompakte und in sich geschlossene Methode zur sicheren Übertragung von Informationen zwischen Parteien in Form eines JSON-Objekts definiert. Diese Informationen können überprüft und als vertrauenswürdig eingestuft werden, da sie digital signiert sind. JWTs können mit einem geheimen (mit dem HMAC-Algorithmus) oder einem öffentlichen/privaten Schlüsselpaar (mit RSA oder ECDSA) signiert werden.
Wenn Token mit öffentlichen/privaten Schlüsselpaaren signiert werden, bestätigt die Signatur auch, dass nur die Partei, die den privaten Schlüssel besitzt, diejenige ist, die sie signiert hat.
Die JWT-Struktur ist folgende
- Header
- Type
- Signing Algorithm (SN)
- Payload
- Registered claims
- Public claims
- Private claims
- Signature
Wobei sich Signature folgendermaßen zusammensetzt.
SN(base64(header) + "." + base64(payload), secret)
Beim Senden eines JWT-Tokens mit HTTP werden alle drei Teile Base64-kodiert und zu einer Zeichenkette zusammengefügt. Z.B.
Authorization: Bearer xxxx.yyyy.zzzz
where xxxx = base64(Header)
yyyy = base64(payload)
zzzz = base64(Signature)
Die technische Dokumentation finden Sie unter JWT.
SAML
Die Security Assertion Markup Language (SAML) ist ein offener Federated-Identity Standard für den Austausch von Autorisierungs- und Authentifizierungsdaten zwischen einem Service-Provider (SP) und einem Identity-Provider (IDP). SAML ist ein XML-basiertes Framework und bietet wie OIDC einen einzigen Authentifizierungspunkt unter Verwendung eines Autorisierungsservers oder IDP. Daher liegt ihr Zweck wie auch bei OIDC in SSO (Single Sign On) oder allgemeiner in der Identifizierung von Benutzern.
Es gibt hauptsächlich zwei Arten von SAML-Authentifizierungsabläufen, einen vom IDP initiierten und einen vom SP initiierten Ablauf. Z.B.
Diese Illustration zeigt einen vom SP initiierten Ablauf, bei dem der Client versucht, auf eine geschützte Ressource zuzugreifen.
Dieser Fluss sieht dem oben gezeigten OIDC- oder OAuth-Fluss sehr ähnlich. Der Hauptunterschied besteht darin, dass der Zugriff auf die Ressource nicht über einen Token, sondern über eine SAML-Assertion gewährt wird. Darüber hinaus ist es im Allgemeinen bei der Verwendung von SAML der Benutzer selbst, der versucht, auf eine Ressource zuzugreifen, und daher als Client agiert. Im Falle von OIDC handelt es sich stattdessen um eine Mobile- oder Web-Anwendung.
Die technische Dokumentation finden Sie unter SAML.
SAML Assertion
SAML-Assertions sind im Vergleich zu JWT-Tokens recht komplexe XML-Dateien und werden daher hier nicht ausführlich behandelt.
Wir wollen nur erwähnen, dass SAML es einer Partei ermöglicht, Sicherheitsinformationen in Form von Aussagen über ein Subjekt zu behaupten. Die grobe Struktur sieht wie folgt aus:
<saml:Assertion>
<saml:Subject>
<saml:NameID>
...
</saml:NameID>
</saml:Subject>
<saml:AuthnStatement>
...
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute>
...
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
Das Feld Subject enthält Benutzerinformationen wie Name, E-Mail usw. Das Feld AuthnStatement kann authentifizierungsbezogene Informationen wie die Authentifizierungszeit oder die Sitzungs-ID enthalten. Das Feld AttributeStatement enthält themenbezogene Attribute wie Statusinformationen des Benutzers usw.
Fazit
Bisher haben wir die verschiedenen Schemata für die Authentifizierung über HTTP und die gebräuchlichsten Protokolle, die auf HTTP aufbauen, wie OAuth, OpenID Connect, welches eine Erweiterung von OAuth und SAML ist, diskutiert.
Da OpenID Connect im Grunde genommen OAuth mit einem zusätzlichen Authentifizierung-Layer ist, reicht es am Ende aus, die Unterschiede zwischen OpenID Connect und SAML zu vergleichen.
OpenID Connect | SAML |
Die Authentifizierung erfolgt über ein ID-Token. Dies ist ein signiertes JSON-Dokument, das den Betreff, den Aussteller und die Authentifizierungsinformationen enthält | Die Authentifizierung erfolgt über eine „Assertion“ eines signierten XML-Dokuments mit den Informationen zum Thema (wer authentifiziert wurde) |
Im Allgemeinen wird der Benutzer vom Client zum OpenID-Provider umgeleitet, um sich anzumelden | Im Allgemeinen wird der Benutzer vom SP zum IDP umgeleitet, um sich anzumelden. |
Wird bei Web- und mobilen Anwendungen verwendet | Wird nur für Webanwendungen verwendet |
JWT hat eine einfachere Struktur und ist leichter als XML |