CORS in AMP
Viele AMP Komponenten und Erweiterungen nutzen Remote Endpoints mithilfe von CORS Anfragen (Cross-Origin Resource Sharing). In diesem Dokument werden die wichtigsten Aspekte der Verwendung von CORS in AMP erläutert. Informationen zu CORS selbst finden Sie in der W3 CORS Spezifikation.
Warum brauche ich CORS für meine eigene Quelle?
Vielleicht wunderst du dich, warum du CORS für Anfragen an deine eigene Quelle benötigst. Sehen wir uns das an.
AMP Komponenten, die dynamische Daten abrufen (z. B. amp-form, amp-list usw.), senden CORS Anfragen an Remote Endpoints, um diese Daten abzurufen. Wenn deine AMP Seite solche Komponenten enthält, musst du mit CORS arbeiten, damit diese Anfragen nicht fehlschlagen.
Wir können das anhand eines Beispiels veranschaulichen:
Angenommen, du hast eine AMP Seite, auf der Produkte mit Preisen aufgelistet sind. Um die Preise auf der Seite zu aktualisieren, klickt der Benutzer auf einen Button, der die neuesten Preise von einem JSON Endpoint abruft (über die Komponente amp-list). Das JSON befindet sich in deiner Domäne.
Okay, die Seite ist in meiner Domäne und das JSON ist auch in meiner Domäne. Kein Problem!
Ah, aber wie hat der Benutzer deine AMP Seite erreicht? Hat er auf eine Seite im Cache zugegriffen? Es ist sehr wahrscheinlich, dass der Benutzer nicht direkt auf deine AMP Seite zugegriffen hat, sondern diese über eine andere Plattform entdeckt hat. So verwendet z. B. die Google Suche den Google AMP Cache, um AMP Seiten schnell zu rendern. Hierbei handelt es sich um zwischengespeicherte Seiten, die aus dem Google AMP Cache bereitgestellt werden – einer anderen Domäne. Wenn der Benutzer auf den Button klickt, um die Preise auf deiner Seite zu aktualisieren, sendet die AMP Seite im Cache eine Anfrage an deine Ursprungsdomäne, um die Preise abzurufen. Dies ist eine Nichtübereinstimmung der Quellen (Cache -> Ursprungsdomäne). Um solche Cross-Origin Anfragen zuzulassen, musst du mit CORS arbeiten. Andernfalls schlägt die Anfrage fehl.
Okay, was soll ich tun?
- Stelle sicher, dass du bei AMP Seiten, die dynamische Daten abrufen, die Cache Version dieser Seiten testest. Teste sie nicht nur auf deiner eigenen Domäne. (Siehe Abschnitt CORS in AMP testen unten)
- Befolge die Anleitung in diesem Dokument, um CORS Anfragen und Antworten zu verarbeiten.
Verwendung von Cookies für CORS Anfragen
Die meisten AMP Komponenten, die CORS Anfragen verwenden, legen entweder automatisch den Credentials Mode fest oder erlauben dem Autor, ihn optional zu aktivieren. Beispielsweise ruft die Komponente amp-list
dynamische Inhalte von einem CORS JSON Endpoint ab und ermöglicht dem Autor, den Credentials Mode über das Attribut credentials
festzulegen.
Beispiel: Personalisierte Inhalte mithilfe von Cookies in amp-list einfügen
<amp-list credentials="include" src="<%host%>/json/product.json?clientId=CLIENT_ID(myCookieId)" > <template type="amp-mustache"> Your personal offer: ${{price}} </template> </amp-list>
Durch Angabe des Credentials Mode kann die Quelle Cookies in die CORS Anfrage aufnehmen und Cookies in der Antwort platzieren (unterliegt der Beschränkung für Drittanbieter-Cookies).
Beschränkung für Drittanbieter-Cookies
Dieselben im Browser festgelegten Beschränkungen von Drittanbieter-Cookies gelten auch für CORS Anfragen mit Credentials in AMP. Diese Einschränkungen hängen vom Browser und der Plattform ab. Bei einigen Browsern kann die Quelle jedoch nur Cookies verwenden, wenn der Benutzer die Quelle des Erstanbieters bereits in einem Hauptfenster aufgerufen hat. Mit anderen Worten: nachdem der Benutzer die eigentliche Website der Quelle direkt besucht hat. Vor diesem Hintergrund kann ein Dienst, auf den über CORS zugegriffen wird, nicht davon ausgehen, dass Cookies standardmäßig verwendet werden können.
CORS Sicherheit in AMP
Um gültige und sichere Anfragen und Antworten für deine AMP Seiten sicherzustellen, musst du:
Wenn du Node in deinem Backend nutzt, kannst du die AMP CORS Middleware verwenden, die Teil der AMP Toolbox ist.
CORS Anfragen verifizieren
Wenn dein Endpoint eine CORS Anfrage erhält:
- Stelle sicher, dass der CORS
Origin
Header eine zulässige Quelle ist (Quelle des Publishers + AMP Caches). - Wenn kein Origin Header vorhanden ist, überprüfe, ob die Anfrage von derselben Quelle stammt (über
AMP-Same-Origin
).
1) Erlaube Anfragen für bestimmte CORS Quellen
CORS Endpoints empfangen die anfragende Quelle über den Origin
HTTP Header. Endpoints sollten nur folgende Anfragen zulassen: (1) Anfragen von der eigenen Quelle des Publishers, und (2) Anfragen von jeder unter https://cdn.ampproject.org/caches.json aufgeführten cacheDomain
Quelle.
So sollten Endpoints z. B. Anfragen zulassen von:
- der Google AMP Cache Subdomäne:
https://<publisher's domain>.cdn.ampproject.org
(zum Beispielhttps://nytimes-com.cdn.ampproject.org
)
2) Anfragen aus gleicher Quelle erlauben
Für Anfragen aus derselben Quelle, bei denen der Origin
Header fehlt, verwendet AMP den folgenden benutzerdefinierten Header:
AMP-Same-Origin: true
Dieser benutzerdefinierte Header wird von der AMP Runtime gesendet, wenn eine XHR Anfrage an dieselbe Quelle erfolgt (also ein Dokument, das von einer URL bereitgestellt wird, die nicht zum Cache gehört). Erlaube Anfragen, die den Header AMP-Same-Origin:true
enthalten.
CORS Anfrageheader senden
Nach der Verifizierung der CORS Anfrage muss die resultierende HTTP Antwort die folgenden Header enthalten:
Access-Control-Allow-Origin: <origin>
Dieser Header wird von der W3 CORS Spezifikation gefordert, wobei sich origin
auf die anfragende Quelle bezieht, die über den CORS Origin
Anfrageheader zugelassen wurde (z. B. "https://<Subdomäne des Publishers>.cdn.ampproject.org"
).
Obwohl die W3 CORS Spezifikation die Rückgabe des Wertes *
in der Antwort erlaubt, solltest du aus Sicherheitsgründen Folgendes tun:
- Falls der
Origin
Header vorhanden ist, validiere den Wert desOrigin
Headers und gib ihn mittels 'echo' aus.
Anfragen zur Zustandsänderung verarbeiten
Überprüfe Folgendes, bevor du Anfragen verarbeitest, die den Zustand deines Systems ändern könnten (z. B. wenn ein Benutzer eine Mailingliste abonniert oder abbestellt):
Wenn der Origin
Header angegeben wurde:
- Wenn die Quelle mit keinem der folgenden Werte übereinstimmt, muss der Vorgang abgebrochen und eine Fehlerantwort zurückgegeben werden:
<Domäne des Publishers>.cdn.ampproject.org
- die Quelle des Publishers (also deine)
Dabei steht *
für einen Platzhalter, nicht für das eigentliche Sternchen ( * ).
- Andernfalls soll die Anfrage verarbeitet werden.
Wenn der Origin
Header NICHT angegeben wurde:
- Stelle sicher, dass die Anfrage den Header
AMP-Same-Origin: true
enthält. Falls die Anfrage diesen Header nicht enthält, muss der Vorgang abgebrochen und eine Fehlerantwort zurückgegeben werden. - Andernfalls soll die Anfrage verarbeitet werden.
Beispielanleitung: Verarbeitung von CORS Anfragen und Antworten
In CORS Anfragen an deinen Endpoint müssen zwei Szenarien berücksichtigt werden:
- Eine Anfrage aus derselben Quelle
- Eine Anfrage aus einer Quelle im Cache (aus einem AMP Cache).
Sehen wir uns diese Szenarien anhand eines Beispiels an. In unserem Beispiel verwalten wir die Website example.com
, die eine AMP Seite mit dem Namen article-amp.html
hostet. Die AMP Seite enthält eine amp-list
zum Abrufen dynamischer Daten aus der Datei data.json
, die auch auf example.com
gehostet wird. Wir möchten Anfragen von unserer AMP Seite an unsere Datei data.json
verarbeiten. Diese Anfragen können von der AMP Seite in derselben Quelle (nicht im Cache) oder von der AMP Seite in einer anderen Quelle (im Cache) stammen.
Zulässige Quellen
Unter Berücksichtigung dessen, was wir über CORS und AMP wissen (laut CORS Anfragen verifizieren weiter oben), werden wir in unserem Beispiel Anfragen aus den folgenden Domänen zulassen:
example.com
--- Domäne des Publishersexample-com.cdn.ampproject.org
--- Subdomäne von Google AMP Cache
Antwortheader für zulässige Anfragen
Für Anfragen aus den zulässigen Quellen enthält unsere Antwort die folgenden Header:
Access-Control-Allow-Origin: <origin>
Es folgen zusätzliche Antwortheader, die wir in unsere CORS Antwort aufnehmen können:
Access-Control-Allow-Credentials: true Content-Type: application/json Access-Control-Max-Age: <delta-seconds> Cache-Control: private, no-cache
Pseudo CORS Logik
Unsere Logik für die Verarbeitung von CORS Anfragen und Antworten kann in Form des folgenden Pseudocodes vereinfacht werden:
IF CORS header present IF origin IN allowed-origins allow request & send response ELSE deny request ELSE IF "AMP-Same-Origin: true" allow request & send response ELSE deny request
CORS Beispielcode
Es folgt eine JavaScript Beispielfunktion, mit der wir CORS Anfragen und Antworten verarbeiten können:
function assertCors(req, res, opt_validMethods, opt_exposeHeaders) { var unauthorized = 'Unauthorized Request'; var origin; var allowedOrigins = [ 'https://example.com', 'https://example-com.cdn.ampproject.org', 'https://cdn.ampproject.org', ]; var allowedSourceOrigin = 'https://example.com'; //publisher's origin // If same origin if (req.headers['amp-same-origin'] == 'true') { origin = sourceOrigin; // If allowed CORS origin & allowed source origin } else if ( allowedOrigins.indexOf(req.headers.origin) != -1 && sourceOrigin == allowedSourceOrigin ) { origin = req.headers.origin; } else { res.statusCode = 403; res.end(JSON.stringify({message: unauthorized})); throw unauthorized; } res.setHeader('Access-Control-Allow-Credentials', 'true'); res.setHeader('Access-Control-Allow-Origin', origin); }
Hinweis: Ein Beispiel für einen funktionierenden Code findest du unter amp-cors.js.
Szenario 1: Anfrage von einer AMP Seite mit derselben Quelle erhalten
Im folgenden Szenario fordert die Seite article-amp.html
die Datei data.json
an. Die Quellen sind gleich.
Wenn wir die Anfrage genauer ansehen, finden wir Folgendes:
Request URL: https://example.com/data.json Request Method: GET AMP-Same-Origin: true
Da diese Anforderung aus derselben Quelle stammt, gibt es keinen Origin
Header, aber der benutzerdefinierte AMP Anfrageheader von AMP-Same-Origin: true
ist vorhanden. Wir können diese Anfrage zulassen, da sie aus derselben Quelle stammt (https://example.com
).
Unsere Antwortheader wären:
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://example.com
Szenario 2: Anfrage von einer AMP Seite aus dem Cache erhalten
Im folgenden Szenario fordert die Seite article-amp.html
aus dem Google AMP Cache die Datei data.json
an. Die Quellen unterscheiden sich.
Wenn wir die Anfrage genauer ansehen, finden wir Folgendes:
Request URL: https://example.com/data.json Request Method: GET Origin: https://example-com.cdn.ampproject.org
Da diese Anfrage einen Origin
Header enthält, überprüfen wir, ob sie aus einer zulässigen Quelle stammt. In diesem Fall können wir die Anfrage zulassen, da sie aus einer zulässigen Quelle stammt.
Unsere Antwortheader wären:
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://example-com.cdn.ampproject.org
Fonts aus dem Cache verwenden
Der Google AMP Cache speichert AMP HTML Dokumente, Bilder und Schriftarten im Cache, um die Geschwindigkeit der AMP Seite zu optimieren. Wir wollen die AMP Seite nicht nur schnell machen, sondern auch auf die Sicherheit der Ressourcen im Cache achten. Wir werden Änderungen an der Art und Weise vornehmen, wie der AMP Cache die Ressourcen im Cache (hauptsächlich Schriftarten) zurückgibt, indem wir den Wert Access-Control-Allow-Origin
der Quelle berücksichtigen.
Bisheriges Verhalten (vor Oktober 2019)
Wenn eine AMP Seite dabei war, https://example.com/some/font.ttf
aus dem Attribut @font-face src
zu laden, speicherte der AMP Cache die Schriftartendatei im Cache und stellt die Ressource wie unten beschrieben mit dem Platzhalter Access-Control-Allow-Origin
bereit.
- URL
https://example-com.cdn.ampproject.org/r/s/example.com/some/font.tff
- Access-Control-Allow-Origin: *
Neues Verhalten (seit Oktober 2019)
Die aktuelle Implementierung ist zwar permissiv, aber das kann zu einer unerwarteten Nutzung der Schriftarten von Cross-Origin Websites führen. Bei dieser Änderung wird der AMP Cache mit genau demselben Wert für Access-Control-Allow-Origin
antworten wie der Quellserver. Um die Schriftarten ordnungsgemäß aus dem AMP Dokument im Cache zu laden, musst du die AMP Cache Quelle via Header akzeptieren.
Dies wäre eine mögliche Implementierung:
function assertFontCors(req, res, opt_validMethods, opt_exposeHeaders) { var unauthorized = 'Unauthorized Request'; var allowedOrigins = [ 'https://example.com', 'https://example-com.cdn.ampproject.org', ]; // If allowed CORS origin if (allowedOrigins.indexOf(req.headers.origin) != -1) { res.setHeader('Access-Control-Allow-Origin', req.headers.origin); } else { res.statusCode = 403; res.end(JSON.stringify({message: unauthorized})); throw unauthorized; } }
Wenn du beispielsweise /some/font.ttf in https://example.com/amp.html
laden wolltest, sollte der Quellserver mit dem Header Access-Control-Allow-Origin antworten (wie nachfolgend angezeigt).
Access-Control-Allow-Origin
antworten. Der AMP Cache wird diesen Wert ebenfalls zurückgeben, d. h. er antwortet mit Access-Control-Allow-Origin: *
. Wenn du diese Einstellung bereits verwendest, musst du nichts ändern. Diese Änderung ist für Mitte Oktober 2019 geplant. Wir erwarten, dass jeder AMP Publisher, der Schriftarten selbst hostet, überprüft, ob diese betroffen sind.
Implementierungsplan
- 30.09.2019: Das Release ermöglicht eine feinere Kontrolle darüber, für welche Domänen diese Änderung gilt. Dieser Build sollte im Laufe dieser Woche veröffentlicht werden.
- 07.10.2019: Testdomänen werden für manuelle Tests aktiviert.
- 14.10.2019: (jedoch abhängig vom Testverlauf): Das Feature wird für alle veröffentlicht.
Folge dem entsprechenden Issue hier.
CORS in AMP testen
Stelle beim Testen deiner AMP Seiten sicher, dass du auch die Versionen deiner AMP Seiten im Cache testest.
Überprüfe die Seite über die Cache URL
So stellst du sicher, dass deine AMP Seite im Cache korrekt gerendert wird und ordnungsgemäß funktioniert:
- Öffne in deinem Browser die URL, über die der AMP Cache auf deine AMP Seite zugreifen würde. Du kannst Cache URL Format mit diesem Tool von AMP By Example ermitteln.
Zum Beispiel:
- URL:
https://amp.dev/documentation/guides-and-tutorials/start/create/
- AMP Cache URL Format:
https://www-ampproject-org.cdn.ampproject.org/c/s/www.ampproject.org/docs/tutorials/create.html
- Öffne die Entwicklertools deines Browsers und stelle sicher, dass keine Fehler vorliegen und alle Ressourcen korrekt geladen wurden.
Überprüfe deine Server Antwortheader
Mithilfe des Befehls curl
kannst du prüfen, ob dein Server die korrekten HTTP Antwortheader sendet. Gib im Befehl curl
die Anfrage-URL und alle benutzerdefinierten Header an, die du hinzufügen möchtest.
Syntax: curl <request-url> -H <custom-header> - I
Anfrage aus derselben Quelle
Bei einer Same-Origin Anfrage fügt das AMP System den benutzerdefinierten Header AMP-Same-Origin:true
hinzu.
Das ist unser curl Befehl zum Testen einer Anfrage von https://ampbyexample.com
an die Datei examples.json
(in derselben Domäne):
curl 'https://amp.dev/static/samples/json/examples.json' -H 'AMP-Same-Origin: true' -I
Die Ergebnisse des Befehls zeigen die korrekten Antwortheader (Hinweis: Überschüssige Informationen wurden gekürzt):
HTTP/2 200 access-control-allow-headers: Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token access-control-allow-credentials: true access-control-allow-origin: https://ampbyexample.com access-control-allow-methods: POST, GET, OPTIONS
Anfrage von einer AMP Seite aus dem Cache testen
In einer CORS Anfrage, die nicht aus derselben Domäne stammt (d. h. aus dem Cache), ist der origin
Header Teil der Anfrage.
Das ist unser curl Befehl zum Testen einer Anfrage von einer AMP Seite im Google AMP Cache an die Datei examples.json
:
curl 'https://amp.dev/static/samples/json/examples.json' -H 'origin: https://ampbyexample-com.cdn.ampproject.org' -I
Die Ergebnisse des Befehls zeigen die korrekten Antwortheader:
HTTP/2 200
access-control-allow-headers: Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token
access-control-allow-credentials: true
access-control-allow-origin: https://ampbyexample-com.cdn.ampproject.org
access-control-allow-methods: POST, GET, OPTIONS