Best Practices für SPA zur Authentifizierung und Sitzungsverwaltung

308

Was glauben die Leute beim Erstellen von Anwendungen im SPA-Stil mit Frameworks wie Angular, Ember, React usw. als Best Practices für die Authentifizierung und das Sitzungsmanagement? Ich kann mir ein paar Möglichkeiten vorstellen, wie ich das Problem angehen könnte.

  1. Behandeln Sie es nicht anders als die Authentifizierung mit einer normalen Webanwendung, vorausgesetzt, die API und die Benutzeroberfläche haben dieselbe Ursprungsdomäne.

    Dies würde wahrscheinlich ein Sitzungscookie, einen serverseitigen Sitzungsspeicher und wahrscheinlich einen Sitzungs-API-Endpunkt beinhalten, auf den die authentifizierte Web-Benutzeroberfläche zugreifen kann, um aktuelle Benutzerinformationen abzurufen, die bei der Personalisierung helfen oder möglicherweise sogar Rollen / Fähigkeiten auf der Clientseite bestimmen. Der Server würde natürlich weiterhin Regeln zum Schutz des Zugriffs auf Daten durchsetzen. Die Benutzeroberfläche würde diese Informationen nur verwenden, um die Erfahrung anzupassen.

  2. Behandeln Sie es wie einen Drittanbieter-Client, der eine öffentliche API verwendet, und authentifizieren Sie sich mit einem Token-System, das OAuth ähnelt. Dieser Token-Mechanismus wird von der Client-Benutzeroberfläche verwendet, um jede einzelne Anforderung an die Server-API zu authentifizieren.

Ich bin hier nicht wirklich ein Experte, aber # 1 scheint für die überwiegende Mehrheit der Fälle völlig ausreichend zu sein, aber ich würde wirklich gerne mehr erfahrene Meinungen hören.

Chris Nicola
quelle
Ich sehe es so, stackoverflow.com/a/19820685/454252
allenhwkim

Antworten:

477

Diese Frage wurde hier in etwas anderer Form ausführlich behandelt:

RESTful Authentifizierung

Dies spricht es jedoch von der Serverseite an. Schauen wir uns das von der Client-Seite aus an. Bevor wir das tun, gibt es jedoch einen wichtigen Auftakt:

Javascript Crypto ist hoffnungslos

Matasanos Artikel dazu ist berühmt, aber die darin enthaltenen Lektionen sind ziemlich wichtig:

https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/

Zusammenfassen:

  • Ein Man-in-the-Middle-Angriff kann Ihren Kryptocode trivial durch ersetzen <script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>
  • Ein Man-in-the-Middle-Angriff ist für eine Seite, die eine Ressource über eine Nicht-SSL-Verbindung bereitstellt, trivial.
  • Sobald Sie SSL haben, verwenden Sie sowieso echte Krypto.

Und um eine eigene Folgerung hinzuzufügen:

  • Ein erfolgreicher XSS-Angriff kann dazu führen, dass ein Angreifer Code im Browser Ihres Clients ausführt, selbst wenn Sie SSL verwenden. Selbst wenn Sie jede Luke geschlossen haben, kann Ihre Browser-Krypto dennoch fehlschlagen, wenn Ihr Angreifer einen Weg zur Ausführung findet Javascript-Code im Browser eines anderen.

Dies macht viele RESTful-Authentifizierungsschemata unmöglich oder albern, wenn Sie beabsichtigen, einen JavaScript-Client zu verwenden. Lass uns nachsehen!

HTTP Basic Auth

In erster Linie HTTP Basic Auth. Das einfachste Schema: Geben Sie bei jeder Anfrage einfach einen Namen und ein Passwort ein.

Dies erfordert natürlich unbedingt SSL, da Sie bei jeder Anfrage einen Base64 (reversibel) -codierten Namen und ein Kennwort übergeben. Jeder, der in der Leitung zuhört, kann den Benutzernamen und das Passwort trivial extrahieren. Die meisten Argumente für "Basic Auth is unsicher" stammen von einem Ort mit "Basic Auth over HTTP", was eine schreckliche Idee ist.

Der Browser bietet integrierte HTTP Basic Auth-Unterstützung, ist aber hässlich und sollte wahrscheinlich nicht für Ihre App verwendet werden. Die Alternative besteht jedoch darin, Benutzername und Passwort in JavaScript zu speichern.

Dies ist die RESTful-Lösung. Der Server benötigt keinerlei Kenntnis des Status und authentifiziert jede einzelne Interaktion mit dem Benutzer. Einige REST-Enthusiasten (meistens Strohmänner) bestehen darauf, dass die Aufrechterhaltung eines Zustands Häresie ist und beim Aufschäumen des Mundes schäumt, wenn Sie an eine andere Authentifizierungsmethode denken. Diese Art der Einhaltung von Standards bietet theoretische Vorteile - sie wird von Apache sofort unterstützt - Sie können Ihre Objekte als Dateien in Ordnern speichern, die durch .htaccess-Dateien geschützt sind, wenn Ihr Herz dies wünscht!

Das Problem ? Sie speichern auf der Clientseite einen Benutzernamen und ein Kennwort zwischen. Dies gibt evil.ru einen besseren Überblick - selbst die grundlegendsten XSS-Schwachstellen können dazu führen, dass der Client seinen Benutzernamen und sein Kennwort auf einen bösen Server überträgt. Sie könnten versuchen, dieses Risiko durch Hashing und Salting des Passworts zu verringern, aber denken Sie daran: JavaScript Crypto ist hoffnungslos . Sie könnten dieses Risiko verringern, indem Sie es der Basisauthentifizierungsunterstützung des Browsers überlassen, aber wie bereits erwähnt hässlich wie Sünde.

HTTP Digest Auth

Ist eine Digest-Authentifizierung mit jQuery möglich?

Eine "sicherere" Authentifizierung, dies ist eine Anfrage / Antwort-Hash-Herausforderung. Außer JavaScript Crypto ist hoffnungslos , funktioniert also nur über SSL und Sie müssen den Benutzernamen und das Kennwort auf der Clientseite zwischenspeichern, was es komplizierter als HTTP Basic Auth macht, aber nicht sicherer .

Abfrageauthentifizierung mit zusätzlichen Signaturparametern.

Eine weitere "sicherere" Authentifizierung, bei der Sie Ihre Parameter mit Nonce- und Timing-Daten verschlüsseln (zum Schutz vor Wiederholungs- und Timing-Angriffen) und die senden. Eines der besten Beispiele hierfür ist das OAuth 1.0-Protokoll, das meines Wissens eine ziemlich einfache Möglichkeit darstellt, die Authentifizierung auf einem REST-Server zu implementieren.

http://tools.ietf.org/html/rfc5849

Oh, aber es gibt keine OAuth 1.0-Clients für JavaScript. Warum?

JavaScript Crypto ist hoffnungslos , denken Sie daran. JavaScript kann ohne SSL nicht an OAuth 1.0 teilnehmen, und Sie müssen den Benutzernamen und das Kennwort des Clients weiterhin lokal speichern. Dies entspricht der Kategorie Digest Auth. Es ist komplizierter als HTTP Basic Auth, aber nicht sicherer .

Zeichen

Der Benutzer sendet einen Benutzernamen und ein Kennwort und erhält im Gegenzug ein Token, mit dem Anforderungen authentifiziert werden können.

Dies ist geringfügig sicherer als HTTP Basic Auth, da Sie die vertraulichen Daten verwerfen können, sobald die Transaktion zwischen Benutzername und Kennwort abgeschlossen ist. Es ist auch weniger RESTful, da Token "Status" darstellen und die Serverimplementierung komplizierter machen.

SSL immer noch

Das Problem ist jedoch, dass Sie immer noch diesen anfänglichen Benutzernamen und das Passwort senden müssen, um ein Token zu erhalten. Vertrauliche Informationen berühren immer noch Ihr kompromittierbares JavaScript.

Um die Anmeldeinformationen Ihres Benutzers zu schützen, müssen Sie Angreifer weiterhin von Ihrem JavaScript fernhalten und weiterhin einen Benutzernamen und ein Kennwort über das Netzwerk senden. SSL erforderlich.

Token-Ablauf

Es ist üblich, Token-Richtlinien wie "Hey, wenn dieses Token zu lange existiert, verwerfen Sie es und lassen Sie den Benutzer erneut authentifizieren." Durchzusetzen. oder "Ich bin mir ziemlich sicher, dass die einzige IP-Adresse, die dieses Token verwenden darf, XXX.XXX.XXX.XXX" ist. Viele dieser Richtlinien sind ziemlich gute Ideen.

Feuersheeping

Die Verwendung eines Tokens ohne SSL ist jedoch weiterhin anfällig für einen Angriff namens "Sidejacking": http://codebutler.github.io/firesheep/

Der Angreifer erhält die Anmeldeinformationen Ihres Benutzers nicht, kann sich jedoch als Benutzer ausgeben, was ziemlich schlecht sein kann.

tl; dr: Das Senden unverschlüsselter Token über das Kabel bedeutet, dass Angreifer diese Token leicht schnappen und sich als Benutzer ausgeben können. FireSheep ist ein Programm, das dies sehr einfach macht.

Eine separate, sicherere Zone

Je größer die Anwendung ist, die Sie ausführen, desto schwieriger ist es sicherzustellen, dass sie keinen Code einfügen können, der die Verarbeitung vertraulicher Daten ändert. Vertrauen Sie Ihrem CDN absolut? Ihre Werbetreibenden? Ihre eigene Codebasis?

Häufig für Kreditkartendaten und seltener für Benutzername und Kennwort - Einige Implementierer speichern die Eingabe sensibler Daten auf einer vom Rest ihrer Anwendung getrennten Seite. Diese Seite kann streng kontrolliert und so gut wie möglich gesperrt werden, vorzugsweise auf einer ist schwierig, Benutzer mit Phishing.

Cookie (bedeutet nur Token)

Es ist möglich (und üblich), das Authentifizierungstoken in ein Cookie einzufügen. Dies ändert keine der Eigenschaften von auth mit dem Token, es ist eher eine bequeme Sache. Alle vorherigen Argumente gelten weiterhin.

Sitzung (bedeutet immer noch nur Token)

Session Auth ist nur eine Token-Authentifizierung, aber mit ein paar Unterschieden, die es etwas anders erscheinen lassen:

  • Benutzer beginnen mit einem nicht authentifizierten Token.
  • Das Backend verwaltet ein Statusobjekt, das an das Token eines Benutzers gebunden ist.
  • Der Token wird in einem Cookie bereitgestellt.
  • Die Anwendungsumgebung abstrahiert die Details von Ihnen.

Abgesehen davon unterscheidet es sich jedoch nicht wirklich von Token Auth.

Dies geht noch weiter von einer RESTful-Implementierung entfernt - mit Statusobjekten gehen Sie auf einem Stateful-Server immer weiter auf dem Weg des einfachen alten RPC.

OAuth 2.0

OAuth 2.0 befasst sich mit dem Problem: "Wie gewährt Software A Software B Zugriff auf die Daten von Benutzer X, ohne dass Software B Zugriff auf die Anmeldeinformationen von Benutzer X hat?"

Die Implementierung ist nur eine Standardmethode für einen Benutzer, um ein Token zu erhalten, und dann für einen Drittanbieter, der sagt: "Ja, dieser Benutzer und dieses Token stimmen überein, und Sie können jetzt einige ihrer Daten von uns erhalten."

Grundsätzlich ist OAuth 2.0 jedoch nur ein Token-Protokoll. Es weist die gleichen Eigenschaften wie andere Token-Protokolle auf - Sie benötigen immer noch SSL, um diese Token zu schützen - es ändert lediglich die Art und Weise, wie diese Token generiert werden.

OAuth 2.0 kann Ihnen auf zwei Arten helfen:

  • Bereitstellung von Authentifizierung / Informationen für andere
  • Authentifizierung / Informationen von anderen erhalten

Aber wenn es darauf ankommt, benutzt du nur ... Token.

Zurück zu Ihrer Frage

Die Frage, die Sie stellen, lautet also: "Soll ich mein Token in einem Cookie speichern und die automatische Sitzungsverwaltung meiner Umgebung für die Details sorgen, oder sollte ich mein Token in Javascript speichern und diese Details selbst verarbeiten?"

Und die Antwort lautet: Tu, was dich glücklich macht .

Die Sache mit der automatischen Sitzungsverwaltung ist jedoch, dass hinter den Kulissen für Sie viel Magie passiert. Oft ist es besser, die Kontrolle über diese Details selbst zu haben.

Ich bin 21, also ist SSL ja

Die andere Antwort lautet: Verwenden Sie https für alles, oder Räuber stehlen die Passwörter und Token Ihrer Benutzer.

Curtis Lassam
quelle
3
Gute Antwort. Ich schätze die Gleichwertigkeit zwischen Token-Authentifizierungssystemen und der grundlegenden Cookie-Authentifizierung (die häufig in das Web-Framework integriert ist). Das ist genau das, wonach ich gesucht habe. Ich weiß es zu schätzen, dass Sie auch so viele potenzielle Themen berücksichtigen. Prost!
Chris Nicola
11
Ich weiß, dass es eine Weile her ist, aber ich frage mich, ob dies um JWT erweitert werden sollte. auth0.com/blog/2014/01/07/…
Chris Nicola
14
Token It's also less RESTful, as tokens constitute "state and make the server implementation more complicated." (1) Für REST muss der Server zustandslos sein. Ein clientseitig gespeichertes Token repräsentiert den Status für den Server in keiner sinnvollen Weise. (2) Etwas komplizierterer serverseitiger Code hat nichts mit RESTfulness zu tun.
Suppenhund
10
lol_nope_send_it_to_me_insteadIch habe den Namen dieser Funktion geliebt: D
Leo
6
Eine Sache, die Sie anscheinend übersehen: Cookies sind XSS-sicher, wenn sie als httpOnly markiert sind, und können mit Secure und Samesite weiter gesperrt werden. Und die Handhabung von Cookies gibt es schon viel länger === mehr kampferprobt. Sich auf JS und lokalen Speicher zu verlassen, um die Tokensicherheit zu gewährleisten, ist ein Kinderspiel.
Martijn Pieters
57

Sie können die Sicherheit im Authentifizierungsprozess mithilfe von JWT (JSON Web Tokens) und SSL / HTTPS erhöhen .

Die Basisauthentifizierung / Sitzungs-ID kann gestohlen werden über:

  • MITM-Angriff (Man-In-The-Middle) - ohne SSL / HTTPS
  • Ein Eindringling, der Zugriff auf den Computer eines Benutzers erhält
  • XSS

Mit JWT verschlüsseln Sie die Authentifizierungsdetails des Benutzers, speichern sie im Client und senden sie zusammen mit jeder Anforderung an die API, wo der Server / die API das Token validiert. Es kann nicht ohne den privaten Schlüssel (den der Server / die API geheim speichert) entschlüsselt / gelesen werden . Update lesen .

Der neue (sicherere) Fluss wäre:

Anmeldung

  • Der Benutzer meldet sich an und sendet Anmeldeinformationen an die API (über SSL / HTTPS).
  • Die API erhält Anmeldeinformationen
  • Wenn gültig:
    • Registrieren Sie eine neue Sitzung in der Datenbank. Update lesen
    • Verschlüsseln Sie Benutzer-ID, Sitzungs-ID, IP-Adresse, Zeitstempel usw. in einem JWT mit einem privaten Schlüssel.
  • Die API sendet das JWT-Token zurück an den Client (über SSL / HTTPS).
  • Der Client empfängt das JWT-Token und speichert es in localStorage / Cookie

Jede Anfrage an API

  • Der Benutzer sendet eine HTTP-Anforderung an die API (über SSL / HTTPS) mit dem gespeicherten JWT-Token im HTTP-Header
  • Die API liest den HTTP-Header und entschlüsselt das JWT-Token mit seinem privaten Schlüssel
  • Die API überprüft das JWT-Token, vergleicht die IP-Adresse der HTTP-Anforderung mit der im JWT-Token und prüft, ob die Sitzung abgelaufen ist
  • Wenn gültig:
    • Antwort mit angefordertem Inhalt zurückgeben
  • Wenn ungültig:
    • Ausnahme auslösen (403/401)
    • Markieren Sie das Eindringen in das System
    • Senden Sie eine Warn-E-Mail an den Benutzer.

Aktualisiert am 30.07.15:

JWT-Nutzdaten / -Ansprüche können tatsächlich ohne den privaten Schlüssel (geheim) gelesen werden, und es ist nicht sicher, ihn in localStorage zu speichern. Es tut mir leid wegen dieser falschen Aussagen. Sie scheinen jedoch an einem JWE-Standard (JSON Web Encryption) zu arbeiten .

Ich habe dies implementiert, indem ich Ansprüche (userID, exp) in einem JWT gespeichert, mit einem privaten Schlüssel (geheim) signiert, den die API / das Backend nur kennt, und als sicheres HttpOnly-Cookie auf dem Client gespeichert habe. Auf diese Weise kann es nicht über XSS gelesen und nicht manipuliert werden, da sonst die JWT die Signaturüberprüfung nicht besteht. Durch die Verwendung eines sicheren HttpOnly- Cookies stellen Sie außerdem sicher, dass das Cookie nur über HTTP-Anforderungen (für das Skript nicht zugänglich) und nur über eine sichere Verbindung (HTTPS) gesendet wird.

Aktualisiert am 17.07.16:

JWTs sind von Natur aus staatenlos. Das heißt, sie machen sich selbst ungültig / verfallen. Indem Sie die Sitzungs-ID zu den Ansprüchen des Tokens hinzufügen, machen Sie es statusbehaftet, da seine Gültigkeit jetzt nicht nur von der Signaturüberprüfung und dem Ablaufdatum abhängt, sondern auch vom Sitzungsstatus auf dem Server. Der Vorteil ist jedoch, dass Sie Token / Sitzungen leicht ungültig machen können, was Sie mit zustandslosen JWTs zuvor nicht konnten.

Gaui
quelle
1
Am Ende ist ein JWT aus Sicherheitsgründen immer noch "nur ein Token", denke ich. Der Server könnte die Benutzer-ID, die IP-Adresse, den Zeitstempel usw. weiterhin mit einem undurchsichtigen Sitzungstoken verknüpfen und wäre nicht mehr oder weniger sicher als ein JWT. Die Staatenlosigkeit von JWT erleichtert jedoch die Implementierung.
James
1
@James the JWT hat den Vorteil, dass es überprüfbar ist und wichtige Details enthalten kann. Dies ist sehr nützlich für verschiedene API-Szenarien, z. B. wenn eine domänenübergreifende Authentifizierung erforderlich ist. Etwas, für das eine Sitzung nicht so gut ist. Es ist auch eine definierte (oder zumindest in Bearbeitung befindliche) Spezifikation, die für Implementierungen nützlich ist. Das heißt nicht, dass es besser ist als jede andere gute Token-Implementierung, aber es ist gut definiert und praktisch.
Chris Nicola
1
@ Chris Ja, ich stimme allen Ihren Punkten zu. Der in der obigen Antwort beschriebene Fluss ist jedoch von Natur aus kein sicherer Fluss, wie aufgrund der Verwendung eines JWT behauptet wird. Darüber hinaus kann die JWT in dem oben beschriebenen Schema nur widerrufen werden, wenn Sie JWT eine Kennung zuordnen und den Status auf dem Server speichern. Andernfalls müssen Sie entweder regelmäßig eine neue JWT erhalten, indem Sie einen Benutzernamen / ein Kennwort anfordern (schlechte Benutzererfahrung), oder eine JWT mit einer sehr langen Ablaufzeit ausstellen (schlecht, wenn das Token gestohlen wird).
James
1
Meine Antwort ist nicht 100% richtig, da JWT tatsächlich ohne den privaten Schlüssel (geheim) entschlüsselt / gelesen werden kann und es nicht sicher ist, ihn in localStorage zu speichern. Ich habe dies implementiert, indem ich Ansprüche (userID, exp) in einem JWT gespeichert, mit einem privaten Schlüssel (geheim) signiert, den die API / das Backend nur kennt, und als HttpOnly-Cookie auf dem Client gespeichert habe. Auf diese Weise kann es nicht von XSS gelesen werden. Sie müssen jedoch HTTPS verwenden, da das Token bei einem MITM-Angriff gestohlen werden könnte. Ich werde meine Antwort aktualisieren, um darüber nachzudenken.
Gaui
1
@vsenko Der Cookie wird mit jeder Anfrage vom Client gesendet. Sie greifen nicht über JS auf das Cookie zu, es ist mit jeder HTTP-Anforderung vom Client an die API verknüpft.
Gaui
7

Ich würde mich für das zweite entscheiden, das Tokensystem.

Wussten Sie schon über Ember-Auth oder Ember-Simple-Auth ? Beide verwenden das tokenbasierte System wie die Ember-Simple-Auth-Zustände:

Eine leichte und unauffällige Bibliothek zum Implementieren der tokenbasierten Authentifizierung in Ember.js-Anwendungen. http://ember-simple-auth.simplabs.com

Sie verfügen über Sitzungsverwaltung und lassen sich auch problemlos in vorhandene Projekte integrieren.

Es gibt auch eine Ember App Kit-Beispielversion von ember-simple-auth: Arbeitsbeispiel für ember-app-kit mit ember-simple-auth für die OAuth2-Authentifizierung.

DelphiLynx
quelle