Wir stellen eine API zur Verfügung, die Partner nur für Domains verwenden können, die sie bei uns registriert haben. Der Inhalt ist teilweise öffentlich (wird aber vorzugsweise nur in den uns bekannten Domains angezeigt), ist jedoch für unsere Benutzer größtenteils privat. So:
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 uns bekannte Domänen zu beschränken und vor allem um sicherzustellen, dass die privaten Benutzerdaten nicht für CSRF anfällig sind .
Dieser API-Schlüssel ist in der Tat für jeden sichtbar, wir authentifizieren unseren Partner nicht auf andere Weise und benötigen keinen REFERER . Trotzdem ist es sicher:
Wenn unsere get-csrf-token.js?apiKey=abc123
angefordert wird:
Suchen Sie den Schlüssel abc123
in der Datenbank und erhalten Sie eine Liste der gültigen Domänen für diesen Schlüssel.
Suchen Sie nach dem CSRF-Validierungscookie. Wenn es nicht vorhanden ist, generieren Sie einen sicheren Zufallswert und fügen Sie ihn in ein Nur-HTTP- Sitzungscookie ein. Wenn das Cookie vorhanden war, rufen Sie den vorhandenen Zufallswert ab.
Erstellen Sie ein CSRF-Token aus dem API-Schlüssel und dem Zufallswert aus dem Cookie und signieren Sie es . (Anstatt eine Liste der Token auf dem Server zu führen, signieren wir die Werte. Beide Werte sind im signierten Token lesbar, das ist in Ordnung.)
Stellen Sie die Antwort so ein, dass sie nicht zwischengespeichert wird, fügen Sie das Cookie hinzu und geben Sie ein Skript wie folgt zurück:
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:
Dies verhindert nicht, dass ein serverseitiges Skript eine Anforderung vortäuscht, sondern stellt nur sicher, dass die Domäne übereinstimmt, wenn dies von einem Browser angefordert wird.
Die Same Origin Policy für JavaScript sorgt dafür , dass ein Browser nicht XHR (Ajax) verwenden können , laden und dann die JavaScript - Quelle zu überprüfen. Stattdessen kann ein normaler Browser ihn nur mit <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">
(oder einem dynamischen Äquivalent) laden und führt dann den Code aus. Natürlich sollte der Server nicht unterstützt Cross-Origin Resource Sharing noch JSONP für den generierten JavaScript.
Ein Browserskript kann den Wert von ändern, document.domain
bevor das obige Skript geladen wird. Dieselbe Ursprungsrichtlinie ermöglicht jedoch nur die Verkürzung der Domäne durch Entfernen von Präfixen, z. B. das Umschreiben subdomain.example.com
auf nur example.com
oder myblog.wordpress.com
auf wordpress.com
oder in einigen Browsern sogar bbc.co.uk
auf co.uk
.
Wenn die JavaScript-Datei mit einem serverseitigen Skript abgerufen wird, erhält der Server auch das Cookie. Ein Server eines Drittanbieters kann den Browser eines Benutzers jedoch nicht dazu bringen, dieses Cookie unserer Domain zuzuordnen. Daher kann ein CSRF-Token und ein Validierungscookie, die mit einem serverseitigen Skript abgerufen wurden, nur von nachfolgenden serverseitigen Aufrufen verwendet werden, nicht in einem Browser. 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 entfernen könnte.
Wenn sich ein Benutzer anmeldet, setzen Sie ein Benutzer-Cookie nach Ihren Wünschen. (Der Benutzer hat sich möglicherweise bereits angemeldet, bevor das JavaScript angefordert wurde.)
Alle nachfolgenden API-Anforderungen an den Server (einschließlich GET- und JSONP-Anforderungen) müssen das CSRF-Token, das CSRF-Validierungscookie und (falls angemeldet) das Benutzercookie enthalten. Der Server kann nun bestimmen, ob der Anforderung vertraut werden soll:
Das Vorhandensein eines gültigen CSRF-Tokens stellt sicher, dass das JavaScript aus der erwarteten Domäne geladen wurde , wenn es von einem Browser geladen wurde.
Das Vorhandensein des CSRF-Tokens ohne das Validierungscookie weist auf eine Fälschung hin.
Das Vorhandensein sowohl des CSRF-Tokens als auch des CSRF-Validierungscookies stellt nichts sicher: Dies kann entweder eine gefälschte serverseitige Anforderung oder eine gültige Anforderung von einem Browser sein. (Es kann sich nicht um eine Anfrage eines Browsers handeln, die von einer nicht unterstützten Domain stammt.)
Das Vorhandensein des Benutzercookies 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.
Das Vorhandensein des Benutzercookies ohne das CSRF-Validierungscookie weist auf eine Fälschung hin.
Das Vorhandensein des Benutzer-Cookies stellt sicher, dass die aktuelle Anforderung über einen Browser erfolgt. (Angenommen, ein Benutzer würde seine Anmeldeinformationen nicht auf einer unbekannten Website eingeben, und vorausgesetzt, es ist uns egal, ob Benutzer ihre eigenen Anmeldeinformationen verwenden, um eine serverseitige Anfrage zu stellen.) Wenn wir auch das CSRF-Validierungscookie haben, war dies das CSRF-Validierungscookie auch über einen Browser empfangen. Als nächstes, wenn wir auch ein CSRF-Token mit einer gültigen Signatur haben, undDie Zufallszahl im CSRF-Validierungscookie stimmt mit der in diesem CSRF-Token überein. Das JavaScript für dieses Token wurde dann auch während derselben früheren Anforderung empfangen, bei der das CSRF-Cookie gesetzt wurde, und verwendet daher auch einen Browser. Dies impliziert dann auch, dass der obige JavaScript-Code ausgeführt wurde, bevor das Token gesetzt wurde, und dass zu diesem Zeitpunkt die Domäne für den angegebenen API-Schlüssel gültig war.
Also: Der Server kann jetzt den API-Schlüssel vom signierten Token sicher verwenden.
Wenn der Server der Anforderung zu irgendeinem Zeitpunkt nicht vertraut, wird ein 403 Forbidden zurückgegeben. Das Widget kann darauf reagieren, indem es dem Benutzer eine Warnung anzeigt.
Es ist nicht erforderlich, das CSRF-Validierungscookie zu signieren, da wir es mit dem signierten CSRF-Token vergleichen. Wenn Sie das Cookie nicht signieren, wird jede HTTP-Anforderung kürzer und die Serverüberprüfung etwas schneller.
Das generierte CSRF-Token ist unbegrenzt gültig, jedoch nur in Kombination mit dem Validierungscookie, also effektiv, bis der Browser geschlossen wird.
Wir könnten die Lebensdauer der Signatur des Tokens begrenzen. Wir könnten das CSRF-Validierungscookie löschen, wenn sich der Benutzer abmeldet, um die OWASP-Empfehlung zu erfüllen . Und um die Zufallszahl pro Benutzer nicht zwischen mehreren Partnern zu teilen, könnte man den API-Schlüssel zum Cookie-Namen hinzufügen. Aber selbst dann kann man das CSRF-Validierungscookie nicht einfach aktualisieren, wenn ein neues Token angefordert wird, da Benutzer möglicherweise dieselbe Site in mehreren Fenstern durchsuchen und ein einzelnes Cookie freigeben (das beim Aktualisieren in allen Fenstern aktualisiert wird, wonach das 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 clientseitige Widgets , von denen ich die JavaScript-Idee erhalten habe. Für die serverseitige Verwendung der API, bei der wir uns nicht auf den JavaScript-Code verlassen können, um die Domäne einzuschränken, verwenden wir geheime Schlüssel anstelle der öffentlichen API-Schlüssel.