wake-up-neo.com

Wie soll ein Facebook-Nutzerzugriffstoken serverseitig verbraucht werden?

Vorwort

Ich entwickle mehrere Webservices und eine Handvoll Clients (Web-App, Mobile usw.), die über HTTP (s) mit diesen Services kommunizieren. Meine aktuelle Aufgabe besteht darin, eine Authentifizierungs- und Autorisierungslösung für das Produkt zu entwerfen. Ich habe mich entschieden, externe Identitätsanbieter wie Facebook, Google, Microsoft, Twitter und dergleichen für die Authentifizierung zu nutzen.

Ich versuche das Problem zu lösen: "Wenn eine Anfrage an meinen Server kommt, woher weiß ich, wer der Benutzer ist und wie kann ich sicher sein?". Weitere Fragen weiter unten ...

Anforderungen

  1. Verlassen Sie sich auf externe Identitäten, um anzugeben, mit wem ich es zu tun habe ('userId' ist im Wesentlichen alles, was mich interessiert).
  2. Das System sollte eine tokenbasierte Authentifizierung verwenden (im Gegensatz zu beispielsweise Cookies oder Basisauthentifizierung).

    Ich bin der Meinung, dass dies die richtige Wahl für die Skalierung über mehrere Clients und Server hinweg ist, während eine lose Kopplung bereitgestellt wird.

Workflow

Basierend auf meinem Verständnis der tokenbasierten Authentifizierung stelle ich mir den Workflow im Folgenden vor. Konzentrieren wir uns zunächst auf Facebook in einem Webbrowser . Ich gehe davon aus, dass andere externe Identitätsanbieter über ähnliche Funktionen verfügen sollten, die ich jedoch noch nicht bestätigt habe.

Beachten Sie, dass ich zum Zeitpunkt des Schreibens Folgendes von der Facebook-Anmeldeversion 2.2 ausführe

  1. Client: Startet die Anmeldung bei Facebook mit dem JavaScript SDK
  2. Facebook: Benutzer authentifiziert und genehmigt App-Berechtigungen (um beispielsweise auf das öffentliche Profil des Benutzers zuzugreifen)
  3. Facebook: Sendet eine Antwort an den Client, die das Zugriffstoken, die ID und die signierte Anforderung des Benutzers enthält
  4. Client: Speichert das Benutzerzugriffstoken in einer Browsersitzung ( wird bequem vom SDK verarbeitet )
  5. Client: Fordert bei meinem Webdienst eine sichere Ressource an, indem der Zugriffstoken des Benutzers im Autorisierungsheader + die ID des Benutzers (im benutzerdefinierten Header) gesendet wird möglicherweise)
  6. Server: Liest das Benutzerzugriffstoken aus dem Anforderungsheader und leitet die Überprüfung ein, indem eine Anforderung an die von Facebook bereitgestellte debug_token-Grafik-API gesendet wird
  7. Facebook: Antwortet dem Server mit den Benutzerzugriffstoken-Informationen (enthält appId und userId)
  8. Server: Verifiziert das Token, indem die AppId mit der erwarteten (ihr bekannten) und die UserId mit der auf der Client-Anfrage gesendeten verglichen wird
  9. Server: Antwortet dem Client mit der angeforderten Ressource (unter der Annahme, dass der Autorisierungspfad fehlerfrei ist)

Ich stelle mir vor, dass die Schritte 5 bis 9 für nachfolgende Anfragen an den Server wiederholt werden (während das Zugriffstoken des Benutzers gültig ist - nicht abgelaufen, von der FB-Seite widerrufen, App-Berechtigungen geändert usw.).

Hier ist ein Diagramm, das Ihnen dabei hilft, die Schritte zu befolgen. Bitte haben Sie Verständnis dafür, dass dieses System keine einseitige Anwendung (SPA) ist . Die genannten Webdienste sind API-Endpunkte, die JSON-Daten im Wesentlichen an Clients zurückliefern. Sie liefern kein HTML/JS/CSS (mit Ausnahme der Web-Client-Server).

Workflow diagram

Fragen

  1. Gibt es zuallererst offensichtliche Lücken/Grubenstürze bei dem beschriebenen Ansatz, der auf meinem Vorwort und meinen Anforderungen basiert?

  2. Ist eine ausgehende Anfrage an Facebook zur Überprüfung des Zugriffstokens (Schritte 6-8 oben) pro Clientanfrage erforderlich/empfohlen?

    Ich weiß zumindest, dass ich das Zugriffstoken überprüfen muss, das von der Clientanforderung stammt. Der empfohlene Ansatz für spätere Überprüfungen nach dem ersten ist mir jedoch nicht bekannt. Wenn es typische Muster gibt, bin ich daran interessiert, davon zu hören. Ich verstehe, dass sie anwendungsabhängig sein können, basierend auf meinen Anforderungen. Ich weiß jedoch noch nicht, wonach ich suchen soll. Ich werde die Due Diligence durchführen, sobald ich eine Grundidee habe.

    Zum Beispiel mögliche Gedanken:

    • Hash das Paar Zugriffstoken + Benutzer-ID nach Abschluss der ersten Überprüfung und speichern Sie es in einem verteilten Cache (auf den alle Webserver zugreifen können), dessen Ablauf dem Ablauf von Zugriffstoken entspricht. Bei nachfolgenden Anforderungen von den Clients wird das Paar Zugriffstoken + Benutzer-ID gehasht und überprüft, ob es im Cache vorhanden ist. Falls vorhanden, wird die Anforderung autorisiert. Andernfalls wenden Sie sich an die Facebook-Grafik-API, um das Zugriffstoken zu bestätigen. Ich gehe davon aus, dass diese Strategie durchführbar ist, wenn ich HTTPS verwende (was ich auch sein werde). Wie aber ist die Leistung zu vergleichen?

    • Die akzeptierte Antwort in diese StackOverflow-Frage empfiehlt, ein benutzerdefiniertes Zugriffstoken zu erstellen, nachdem die erste Überprüfung des Facebook-Benutzertokens abgeschlossen ist. Das benutzerdefinierte Token wird dann für nachfolgende Anforderungen an den Client gesendet. Ich frage mich jedoch, ob dies komplexer ist als die obige Lösung. Dies würde die Implementierung eines eigenen Identitätsanbieters erfordern (etwas, das ich vermeiden möchte, weil ich in erster Linie externe Identitätsanbieter verwenden möchte…). Gibt es irgendeinen Grund für diesen Vorschlag?

  3. Befindet sich das Feld "signedRequest" in der Antwort in Schritt 3 oben (erwähnt hier ), das dem signierten Anforderungsparameter hier im Anmeldefluss für Spiele entspricht?

    Sie scheinen als gleichwertig zu gelten, da die ersteren in der Dokumentation auf die letzteren verweisen. Ich bin jedoch überrascht, dass die auf der Spieleseite erwähnte Verifizierungsstrategie im Abschnitt "Manuelles Erstellen eines Anmeldeflusses" Seite der Webdokumentation nicht erwähnt wird.

  4. Wenn die Antwort auf # 3 "Ja" lautet, kann dieselbe Strategie zur Identitätsbestätigung angewendet werden, bei der die Signatur decodiert und mit der erwarteten Verwendung auf der Serverseite verglichen wird?

    Decode & compare from FB docs

    Ich frage mich, ob dies genutzt werden kann, anstatt einen ausgehenden Aufruf der debug_token-Diagramm-API (Schritt 6 oben) zu tätigen, um das empfohlene Zugriffstoken zu bestätigen hier :

    debug_token graph API from FB docs

    Natürlich müsste, um den Vergleich auf der Serverseite durchzuführen, der signierte Anfrageteil zusammen mit der Anfrage an den Server gesendet werden (Schritt 5 oben). Neben der Machbarkeit ohne Einbußen bei der Sicherheit frage ich mich, wie sich die Leistung im Vergleich zu ausgehenden Anrufen verhält.

  5. Wenn ich schon dabei bin, in welchem ​​Szenario/zu welchem ​​Zweck würden Sie beispielsweise das Zugriffstoken eines Benutzers auf eine Datenbank beibehalten? Ich sehe kein Szenario, in dem ich dies tun müsste, kann jedoch etwas übersehen. Ich bin gespannt, ob einige häufige Szenarien darin bestehen könnten, einige Gedanken zu spark.

Vielen Dank!

64
Scott Lin

Nach Ihrer Beschreibung würde ich vorschlagen, einen serverseitigen Anmeldefluss wie in beschrieben zu verwenden

damit befindet sich das Token bereits auf Ihrem Server und muss nicht vom Client übergeben werden. Wenn Sie nicht verschlüsselte Verbindungen verwenden, kann dies ein Sicherheitsrisiko darstellen (z. B. bei Man-in-the-Middle-Angriffen).

Die Schritte wären:

(1) Leute einloggen

Sie müssen die Berechtigung, die Sie von den Benutzern erhalten möchten, im Parameter scope angeben. Die Anfrage kann einfach über einen normalen Link ausgelöst werden:

GET https://www.facebook.com/dialog/oauth?
    client_id={app-id}
   &redirect_uri={redirect-uri}
   &response_type=code
   &scope={permission_list}

Sehen

(2) Bestätigen Sie die Identität

GET https://graph.facebook.com/oauth/access_token?
    client_id={app-id}
   &redirect_uri={redirect-uri}
   &client_secret={app-secret}
   &code={code-parameter}

(3) Überprüfen Sie das Zugriffstoken

Sie können den Token, wie Sie bereits in Ihrer Frage gesagt haben, über einsehen

GET /debug_token?input_token={token-to-inspect}
    &access_token={app-token-or-admin-token}

Dies sollte nur serverseitig erfolgen, da Sie sonst Ihr App-Zugriffstoken für Endbenutzer sichtbar machen würden (keine gute Idee!).

Sehen

(4) Erweiterung des Zugriffstokens

Sobald Sie den (kurzlebigen) Token erhalten haben, können Sie einen Anruf tätigen, um den Token wie in beschrieben zu erweitern

wie die folgenden:

GET /oauth/access_token?grant_type=fb_exchange_token
    &client_id={app-id}
    &client_secret={app-secret}
    &fb_exchange_token={short-lived-token}

(5) Speichern von Zugriffstoken

In Bezug auf das Speichern der Token auf dem Server schlägt FB vor, dies zu tun:

(6) Behandlung abgelaufener Zugriffstoken

Da FB Sie nicht benachrichtigt, wenn ein Token abgelaufen ist (und wenn Sie das Ablaufdatum nicht speichern und es mit dem aktuellen Zeitstempel vergleichen, bevor Sie einen Anruf tätigen), ist es möglich, dass Sie Fehlermeldungen von FB erhalten, wenn das Token ungültig wurde (nach max. 60 Tagen). Der Fehlercode lautet 190:

{
  "error": {
    "message": "Error validating access token: Session has expired at unix 
                time SOME_TIME. The current unix time is SOME_TIME.", 
    "type": "OAuthException", 
    "code": 190
  }
}

Sehen

Wenn das Zugriffstoken ungültig wird, muss sich die Person erneut anmelden. Anschließend können Sie erneut API-Aufrufe für sie durchführen. Der Login-Flow, den Ihre App für neue Benutzer verwendet, sollte bestimmen, welche Methode Sie anwenden müssen.

23
Tobi
  1. Ich sehe keine grellen Lücken/Grubenstürze, aber ich bin kein Sicherheitsexperte.
  2. Sobald Ihr Server das angegebene Token überprüft hat (Schritt 8), wie Sie sagten:

In der akzeptierten Antwort in dieser StackOverflow-Frage wird empfohlen, ein benutzerdefiniertes Zugriffstoken zu erstellen, nachdem die erste Überprüfung des Facebook-Benutzertokens abgeschlossen ist. Das benutzerdefinierte Token wird dann für nachfolgende Anforderungen an den Client gesendet. Ich frage mich jedoch, ob dies komplexer ist als die obige Lösung. Dies würde die Implementierung eines eigenen Identitätsanbieters erfordern (etwas, das ich vermeiden möchte, weil ich in erster Linie externe Identitätsanbieter verwenden möchte…). Gibt es irgendeinen Grund für diesen Vorschlag?

IMHO ist der richtige Weg. Ich würde https://jwt.io/ verwenden, mit dem Sie Werte (z. B. die Benutzer-ID) mit einem geheimen Schlüssel verschlüsseln können. Dann hängt Ihr Client dieses Token an jede Anfrage an. So können Sie die Anfrage überprüfen, ohne sie an Dritte weitergeben zu müssen (Sie benötigen auch keine Datenbankabfragen). Das Schöne dabei ist, dass Sie das Token nicht in Ihrer DB speichern müssen.

Sie können ein Ablaufdatum für das Token definieren, um zu erzwingen, dass sich der Client bei der dritten Partei erneut authentifiziert, wenn Sie dies möchten.

  1. Angenommen, Sie möchten, dass Ihr Server ohne die Interaktion mit dem Client eine Aktion ausführen kann. Zum Beispiel: Open Graph Stories . Da Sie in diesem Szenario etwas im Namen des Benutzers veröffentlichen müssen, benötigen Sie das Zugriffstoken, das in Ihrer Datenbank gespeichert ist.

(Ich kann nicht mit den 3 und 4 Fragen helfen, sorry).

1
ilopezluna