REST API-Autorisierung und -Authentifizierung (Web + Mobile)

70

Ich habe über oAuth, Amazon REST API, HTTP Basic / Digest usw. gelesen, kann aber nicht alles in "Einzelstücke" zusammenfassen. Dies ist wahrscheinlich die nächstgelegene Situation - Erstellen einer API für mobile Anwendungen - Authentifizierung und Autorisierung

Ich möchte eine API-zentrierte Website erstellen - Service. Also (am Anfang) hätte ich eine API im Zentrum und die Website (PHP + MySQL) würde über cURL , Android und iPhone über ihre Netzwerkschnittstellen eine Verbindung herstellen. Also 3 Hauptclients - 3 API-Schlüssel. Und jeder andere Entwickler könnte auch über eine API-Schnittstelle entwickeln und würde seinen eigenen API-Schlüssel erhalten. API-Aktionen werden basierend auf dem UserLevel-Status akzeptiert / abgelehnt. Wenn ich ein Administrator bin, kann ich alles löschen usw. Alle anderen können nur ihre lokalen (Konto-) Daten bearbeiten.

Erstens, Autorisierung - sollte ich oAuth + xAuth oder meine eigene Implementierung verwenden (siehe http://docs.amazonwebservices.com/AmazonCloudFront/latest/DeveloperGuide/RESTAuthentication.html?r=9197 )? Soweit ich weiß, ist der Benutzer des Amazon-Dienstes == API-Benutzer (API-Schlüssel haben) . In meinem Dienst muss ich Standardbenutzer / -konten (die auf der Website registriert sind) und Entwicklerkonten (die ihren API-Schlüssel haben sollten) trennen.

Ich müsste also zuerst den API-Schlüssel autorisieren und dann den Benutzer selbst authentifizieren . Welches Schema sollte ich für die Benutzerauthentifizierung verwenden, wenn ich das Amazon-Schema verwende, um die API-Schlüssel der Entwickler zu überprüfen (deren App zu autorisieren)?

Ich habe gelesen, wie ich ein Token erhalten kann, api.example.org/authnachdem ich (über HTTPS , HTTP Basic) meinen Benutzernamen und mein Passwort veröffentlicht und es dann bei jeder folgenden Anfrage weitergeleitet habe. Wie verwalte ich Token, wenn ich gleichzeitig auf Android und einer Website angemeldet bin ? Was ist mit Man-in-the-Middle-Attacke, wenn ich SSL nur auf erste Anfrage (wenn Benutzername und Passwort übertragen werden) und nur HTTP auf jedem anderen verwende? Ist das nicht ein Problem in diesem Beispiel ? Kennwort zum Schutz eines REST-Dienstes?

svenkapudija
quelle

Antworten:

125

Der beste Weg, einen Schlüssel zu schützen, besteht darin, ihn nicht zu übertragen.

Wir verwenden jedoch normalerweise ein Schema, bei dem jeder "API-Schlüssel" aus zwei Teilen besteht: einer nicht geheimen ID (z. B. 1234) und einem geheimen Schlüssel (z. B. Byte [64]).

  • Wenn Sie einen API-Schlüssel ausgeben, speichern Sie ihn (gesalzen und gehasht) in der Datenbank Ihres Dienstes.
  • Wenn Sie Benutzerkonten (durch ein Kennwort geschützt) ausgeben, speichern Sie die Kennwörter (gesalzen und gehasht) in der Datenbank Ihres Dienstes

Wenn ein Verbraucher zum ersten Mal auf Ihre API zugreift, um eine Verbindung herzustellen, lassen Sie ihn dies tun

  • Senden Sie einen "Benutzername" -Parameter ("john.doe" nicht geheim)
  • Senden Sie einen "APIkeyID" -Parameter ("1234", nicht geheim)

und gib ihn zurück

  • die Salze aus Ihrer Datenbank (Wenn einer der Parameter falsch ist, geben Sie einfach etwas wiederholbares Salz zurück - z. B. sha1 (Benutzername + "notverysecret").
  • Der Zeitstempel des Servers

Der Verbraucher sollte das Salz für die Sitzungsdauer aufbewahren, um die Dinge schnell und reibungslos zu halten, und er sollte den Zeitversatz zwischen Client und Server berechnen und beibehalten.

Der Verbraucher sollte nun die gesalzenen Hashes von API-Schlüssel und Kennwort berechnen. Auf diese Weise hat der Verbraucher genau die gleichen Hashes für Kennwort und API-Schlüssel wie in Ihrer Datenbank gespeichert, jedoch ohne dass jemals etwas Geheimnisvolles über das Kabel geht.

Wenn ein Verbraucher anschließend auf Ihre API zugreift, um echte Arbeit zu leisten, lassen Sie ihn

  • Senden Sie einen "Benutzername" -Parameter ("john.doe" nicht geheim)
  • Senden Sie einen "APIkeyID" -Parameter ("1234", nicht geheim)
  • Senden Sie einen "RequestSalt" -Parameter (Byte [64], zufällig, nicht geheim)
  • Senden Sie einen "RequestTimestamp" -Parameter (berechnet aus der Client-Zeit und dem bekannten Offset).
  • Senden Sie einen "RequestToken" -Parameter (Hash (passwordhash + request_salt + request_timestamp + apikeyhash))

Der Server sollte in der Vergangenheit keine Zeitstempel von mehr als 2 Sekunden akzeptieren, um dies vor einem Wiederholungsangriff zu schützen.

Der Server kann jetzt denselben Hash (passwordhash + request_salt + request_timestamp + apikeyhash) wie der Client berechnen und sicher sein, dass

  • Der Client kennt den API-Schlüssel.
  • Der Client kennt das richtige Passwort
Eugen Rieck
quelle
1
Tolle Antwort, danke. Aber kann ich mich dem "Zeitversatz" entziehen, indem ich sowohl Client als auch Server auf UTC-Zeit stelle? Oder GET-Anfrage als Amazon angeben / datieren, um den Offset zu berechnen?
Svenkapudija
1
Sicher kannst du. Der Zeitstempel ist wichtig für die Wiedergabesicherheit. Was auch immer für Sie funktioniert, ist in Ordnung. Wenn beide Enden NTP haben, können Sie den Versatzberechnungsschritt auslassen.
Eugen Rieck
1
Welche Hashing-Methode verwenden Sie oder nur um sicherzugehen, dass das Salz lang genug ist? Einige sagen, gehen Sie so sicher wie möglich - SHA256 / 512, oder verwenden Sie sogar HMAC?
Svenkapudija
1
Wir verwenden sha256 mit ca. 100 Bytes Salz. Dies sollte astronomische Ressourcen erfordern, um zurückzukehren.
Eugen Rieck
8
Für TLS / SSL gibt es viel zu sagen - ein wichtiger Teil ist, dass es die Vertraulichkeit der gesamten Konversation gewährleistet. Gegen SSL gibt es auch viel zu sagen: Setup kann eine PITA sein, insbesondere bei exotischen Geräten / Betriebssystemen. Wenn Sie nicht die Vertraulichkeit brauchen, aber authentiucation, ist die obige Lösung so gut wie es geht.
Eugen Rieck