REST-Authentifizierung und Offenlegung des API-Schlüssels

Ich habe auf REST gelesen und es gibt eine Menge Fragen zu SO darüber, sowie auf vielen anderen Seiten und Blogs. Obwohl ich diese spezielle Frage aus irgendeinem Grund nie gesehen habe, kann ich mich nicht um dieses Konzept kümmern …

Wenn ich eine RESTful-API erstelle und sie sichern möchte, verwende ich unter anderem ein Sicherheitstoken. Wenn ich andere APIs verwendet habe, gab es ein Token und ein gemeinsames Geheimnis … macht Sinn. Was ich nicht verstehe ist, dass Anfragen zu einem Rest Service Vorgang mittels Javascript (XHR / Ajax) gemacht werden, was verhindern soll, dass jemand mit etwas Einfachem wie FireBug (oder “View Source” im Browser) schnüffelt und Kopieren des API-Schlüssels und dann Identität dieser Person mit Schlüssel und Geheimnis?

Solutions Collecting From Web of "REST-Authentifizierung und Offenlegung des API-Schlüssels"

api secret wird nicht explizit übergeben, secret wird verwendet, um ein Zeichen der aktuellen Anfrage zu erzeugen, auf der Serverseite generiert der Server das Zeichen nach dem gleichen process, wenn die beiden Zeichen übereinstimmen, dann wird die Anfrage erfolgreich authentifiziert – also nur die Das Zeichen wird durch die Anfrage geleitet, nicht das Geheimnis.

Wir stellen eine API bereit, die Partner nur für Domains verwenden können, die sie bei uns registriert haben. Ihr Inhalt ist teilweise öffentlich (aber vorzugsweise nur für die Domains, die wir kennen), ist aber größtenteils privat für unsere Nutzer. Damit:

  • Um festzustellen, was angezeigt wird, muss unser Benutzer bei uns angemeldet sein, dies wird jedoch separat behandelt.

  • Um festzustellen, wo die Daten angezeigt werden, wird ein öffentlicher API-Schlüssel verwendet, um den Zugriff auf bekannte Domänen einzuschränken und vor allem sicherzustellen, dass die Daten des privaten Benutzers nicht für CSRF anfällig sind.

Dieser API-Schlüssel ist in der Tat für jeden sichtbar, wir authentifizieren unseren Partner auf keine andere Weise und wir brauchen REFERER nicht . Trotzdem ist es sicher:

  1. Wenn unser get-csrf-token.js?apiKey=abc123 angefordert wird:

    1. Suchen Sie den Schlüssel abc123 in der database und erhalten Sie eine Liste der gültigen Domänen für diesen Schlüssel.

    2. Suchen Sie nach dem CSRF-validations-Cookie. Wenn es nicht existiert, generieren Sie einen sicheren Zufallswert und legen Sie ihn in einen HTTP-Only- Session-Cookie. Wenn der Cookie vorhanden war, rufen Sie den vorhandenen zufälligen Wert ab.

    3. Erstellen Sie ein CSRF-Token aus dem API-Schlüssel und dem zufälligen Wert aus dem Cookie, und signieren Sie es . (Anstatt eine Liste mit Token auf dem Server zu speichern, signieren wir die Werte. Beide Werte sind im signierten Token lesbar, das ist in Ordnung.)

    4. Stellen Sie die Antwort so ein, dass sie nicht zwischengespeichert wird, fügen Sie das Cookie hinzu und geben Sie ein Skript zurück wie:

       var apiConfig = apiConfig || {}; if(document.domain === 'expected-domain.com' || document.domain === 'www.expected-domain.com') { apiConfig.csrfToken = 'API key, random value, signature'; // Invoke a callback if the partner wants us to if(typeof apiConfig.fnInit !== 'undefined') { apiConfig.fnInit(); } } else { alert('This site is not authorised for this API key.'); } 

    Anmerkungen:

    • Obiges verhindert nicht, dass ein serverseitiges Skript eine Anfrage vortäuscht, sondern stellt nur sicher, dass die Domain übereinstimmt, wenn sie von einem Browser angefordert wird.

    • Die gleiche Ursprungsrichtlinie für JavaScript stellt sicher, dass ein Browser nicht XHR (Ajax) zum Laden und dann zum Überprüfen der JavaScript-Quelle verwenden kann. Stattdessen kann ein normaler Browser sie nur mit (oder einer dynamischen Entsprechung) laden und führt dann die Code. Natürlich sollte Ihr Server die Cross-Origin-Ressourcenfreigabe und JSONP für das generierte JavaScript nicht unterstützen.

    • Ein Browser-Skript kann den Wert von document.domain ändern, bevor das obige Skript geladen wird. Aber die gleiche Ursprungsrichtlinie erlaubt nur das Verkürzen der Domain durch das Entfernen von Präfixen, wie das Umschreiben von myblog.wordpress.com zu nur example.com oder myblog.wordpress.com zu wordpress.com oder in einigen Browsern sogar bbc.co.uk zu co.uk

    • Wenn die JavaScript-Datei mit einem serverseitigen Skript abgerufen wird, erhält der Server auch das Cookie. Ein Drittanbieterserver kann jedoch nicht den Browser eines Benutzers dazu bringen, diesen Cookie mit unserer Domäne zu verknüpfen. Daher können ein CSRF-Token und ein validations-Cookie, die mit einem serverseitigen Skript abgerufen wurden, nur von nachfolgenden serverseitigen Aufrufen und nicht in einem Browser verwendet werden. Solche serverseitigen Aufrufe enthalten jedoch niemals das Benutzer-Cookie und können daher nur öffentliche Daten abrufen. Dies sind die gleichen Daten, die ein serverseitiges Skript direkt von der Website des Partners abkratzen könnte.

  2. Wenn sich ein Benutzer anmeldet, setzen Sie ein Benutzer-Cookie auf die von Ihnen gewünschte Weise. (Der Benutzer hat sich möglicherweise bereits angemeldet, bevor das JavaScript angefordert wurde.)

  3. Alle nachfolgenden API-Anforderungen an den Server (einschließlich GET- und JSONP-Anforderungen) müssen das CSRF-Token, das CSRF-validations-Cookie und (falls angemeldet) das Benutzer-Cookie enthalten. Der Server kann nun feststellen, ob die Anfrage vertrauenswürdig ist:

    1. Das Vorhandensein eines gültigen CSRF-Tokens stellt sicher, dass das JavaScript von der erwarteten Domäne geladen wurde, wenn es von einem Browser geladen wurde.

    2. Das Vorhandensein des CSRF-Tokens ohne den validations-Cookie zeigt eine Fälschung an.

    3. Das Vorhandensein des CSRF-Tokens und des CSRF-validationscookies stellt nichts sicher: Dies könnte entweder eine gefälschte serverseitige Anfrage oder eine gültige Anfrage eines Browsers sein. (Es kann keine Anfrage von einem Browser aus einer nicht unterstützten Domain sein.)

    4. Das Vorhandensein des Benutzer-Cookies stellt sicher, dass der Benutzer angemeldet ist, stellt jedoch nicht sicher, dass der Benutzer Mitglied des angegebenen Partners ist oder dass der Benutzer die richtige Website anzeigt.

    5. Das Vorhandensein des Benutzer-Cookies ohne den CSRF-validations-Cookie weist auf eine Fälschung hin.

    6. Das Vorhandensein des Benutzer-Cookies stellt sicher, dass die aktuelle Anfrage über einen Browser erfolgt. (Angenommen, ein Benutzer würde seine Anmeldeinformationen nicht auf einer unbekannten Website eingeben und davon ausgehen, dass es uns nicht interessiert, dass Benutzer ihre eigenen Anmeldeinformationen verwenden, um eine serverseitige Anforderung zu stellen.) Wenn wir auch das CSRF-validations-Cookie haben, dann war dies das CSRF-validations-Cookie auch erhalten mit einem Browser. Wenn wir dann auch ein CSRF-Token mit einer gültigen Signatur haben und die Zufallszahl im CSRF-validations-Cookie mit der in diesem CSRF-Token übereinstimmt, wurde das JavaScript für dieses Token auch während derselben früheren Anfrage empfangen, während der CSRF Cookie wurde gesetzt, also auch mit einem Browser. Dies bedeutet auch, dass der obige JavaScript-Code ausgeführt wurde, bevor das Token gesetzt wurde, und dass die Domain zu diesem Zeitpunkt für den angegebenen API-Schlüssel gültig war.

      Also: Der Server kann jetzt sicher den API-Schlüssel aus dem signierten Token verwenden.

    7. Wenn der Server zu irgendeinem Zeitpunkt der Anfrage nicht traut, wird 403 Forbidden zurückgegeben. Das Widget kann darauf reagieren, indem es dem Benutzer eine Warnung anzeigt.

Es ist nicht erforderlich, das CSRF-validations-Cookie zu signieren, da wir es mit dem signierten CSRF-Token vergleichen. Wenn der Cookie nicht signiert wird, wird jede HTTP-Anforderung kürzer und die Servervalidierung etwas schneller.

Das generierte CSRF-Token ist unbegrenzt gültig, jedoch nur in Kombination mit dem validations-Cookie, also bis zum Schließen des Browsers.

Wir könnten die Lebensdauer der Signatur des Tokens begrenzen. Wir könnten das CSRF-validations-Cookie löschen, wenn sich der Benutzer abmeldet, um die OWASP-Empfehlung zu erfüllen. Um die Zufallszahl pro Benutzer zwischen mehreren Partnern nicht zu teilen, könnte man den API-Schlüssel zum Cookie-Namen hinzufügen. Aber selbst dann kann man das CSRF-validations-Cookie nicht einfach aktualisieren, wenn ein neues Token angefordert wird, da Benutzer die gleiche Site in mehreren Fenstern durchsuchen und einen einzigen Cookie freigeben (der beim Aktualisieren in allen Fenstern aktualisiert wird) Das JavaScript-Token in den anderen Fenstern würde nicht mehr mit diesem einzelnen Cookie übereinstimmen).

Für diejenigen, die OAuth verwenden, siehe auch OAuth und Client-Side Widgets , von denen ich die JavaScript-Idee bekam. Für die serverseitige Verwendung der API, bei der wir uns nicht auf den JavaScript-Code zur Beschränkung der Domäne verlassen können, verwenden wir geheime Schlüssel anstelle der öffentlichen API-Schlüssel.

Diese Frage hat eine akzeptierte Antwort, aber um zu klären, funktioniert die gemeinsame geheime Authentifizierung wie folgt:

  1. Der Client hat einen öffentlichen Schlüssel, der mit jedem geteilt werden kann, egal, so dass Sie ihn in Javascript einbetten können. Dies wird verwendet, um den Benutzer auf dem Server zu identifizieren.
  2. Der Server hat einen geheimen Schlüssel und dieses Geheimnis muss geschützt werden. Daher erfordert die Authentifizierung mit gemeinsam genutzten Schlüsseln, dass Sie Ihren geheimen Schlüssel schützen können. Daher ist ein öffentlicher JavaScript-Client, der direkt mit einem anderen Dienst verbunden ist, nicht möglich, da Sie einen Server-Mittelsmann benötigen, um das Geheimnis zu schützen.
  3. Der Server signiert die Anforderung unter Verwendung eines Algorithmus, der den geheimen Schlüssel enthält (der geheime Schlüssel ist eine Art von Salz), und vorzugsweise sendet ein Zeitstempel die Anfrage an den Dienst. Der Zeitstempel soll “Replay” -Angriffe verhindern. Eine Signatur einer Anfrage ist nur für ca. n Sekunden gültig. Sie können dies auf dem Server überprüfen, indem Sie den Zeitstempel-Header abrufen, der den Wert des Zeitstempels enthalten sollte, der in der Signatur enthalten war. Wenn dieser Zeitstempel abgelaufen ist, schlägt die Anfrage fehl.
  4. Der Dienst erhält die Anfrage, die nicht nur die Unterschrift enthält, sondern auch alle Felder, die im Klartext signiert wurden.
  5. Der Dienst signiert dann die Anforderung auf die gleiche Weise unter Verwendung des gemeinsamen geheimen Schlüssels und vergleicht die Signaturen.

Ich meine, du meinst Session-Schlüssel nicht API-Schlüssel. Dieses Problem wird vom http-Protokoll geerbt und als Session Hijacking bekannt . Der normale “Workaround” ist, wie auf jeder Website, der Wechsel zu https.

Um den REST-Dienst sicher auszuführen, müssen Sie https und wahrscheinlich die Clientauthentifizierung aktivieren. Aber das ist schließlich jenseits der REST-Idee. REST spricht nie über Sicherheit.

Auf der Serverseite möchten Sie eine ablaufende Sitzungs-ID generieren, die bei der Anmeldung oder Anmeldung an den Client gesendet wird. Der Client kann dann diese Sitzungs-ID als ein gemeinsames Geheimnis verwenden, um nachfolgende Anforderungen zu signieren.

Die Sitzungs-ID wird nur einmal übergeben und dies muss über SSL erfolgen.

Siehe Beispiel hier

Verwenden Sie eine Nonce und einen Zeitstempel, wenn Sie die Anfrage signieren, um ein Session-Hijacking zu verhindern.

Ich werde versuchen, die Frage in ihrem ursprünglichen Kontext zu beantworten. Die Frage ist also “Ist der geheime Schlüssel (API) sicher in JavaScript zu platzieren?”

Meiner Meinung nach ist es sehr unsicher, da es den Zweck der Authentifizierung zwischen den Systemen vereitelt. Da der Schlüssel dem Benutzer zugänglich gemacht wird, kann der Benutzer Informationen abrufen, zu denen er nicht berechtigt ist. In einer typischen Ruhepause basiert die Authentifizierung nur auf dem API-Schlüssel.

Eine Lösung ist meiner Meinung nach, dass der JavaScript-Aufruf die Anfrage im Wesentlichen an eine interne Server-Komponente weitergibt, die dafür verantwortlich ist, einen Restanruf zu tätigen. Die interne Serverkomponente lässt beispielsweise ein Servlet den API-Schlüssel von einer gesicherten Quelle wie einem permission-basierten Dateisystem lesen, in den HTTP-Header einfügen und den externen Restanruf tätigen.

Ich hoffe das hilft.