CORS avec AMP
De nombreux composants et extensions AMP tirent parti des points de terminaison distants en utilisant des requêtes CORS (Cross-Origin Resource Sharing). Ce document explique les aspects clés de l'utilisation de CORS avec AMP. Pour en savoir plus sur CORS lui-même, consultez la spécification W3 CORS .
- Pourquoi ai-je besoin de CORS pour ma propre origine ?
- Utilisation de cookies pour les requêtes CORS
-
Pourquoi ai-je besoin de CORS pour ma propre origine ?
Vous pourriez être confus(e) quant à la raison pour laquelle vous auriez besoin de CORS pour les requêtes vers votre propre origine, explorons cela.
Les composants AMP qui récupèrent des données dynamiques (par exemple, amp-form, amp-list, etc.) adressent des requêtes CORS aux points de terminaison distants pour récupérer les données. Si votre page AMP inclut de tels composants, vous devrez gérer CORS pour que ces requêtes n'échouent pas.
Illustrons cela avec un exemple :
Supposons que vous ayez une page AMP qui répertorie les produits avec des prix. Pour mettre à jour les prix sur la page, l'utilisateur clique sur un bouton, qui récupère les derniers prix à partir d'un point de terminaison JSON (via le composant amp-list). Le JSON est sur votre domaine.
D'accord, donc la page est sur mon domaine et le JSON est sur mon domaine. Je ne vois aucun problème !
Ah, mais comment votre utilisateur est-il arrivé sur votre page AMP ? S'agit-il d'une page mise en cache à laquelle il accède ? Il est fort probable que votre utilisateur n'ait pas accédé directement à votre page AMP, mais qu'il ait découvert votre page via une autre plateforme. Par exemple, la recherche Google utilise le cache AMP Google pour afficher rapidement les pages AMP ; ce sont des pages mises en cache qui sont diffusées à partir du cache AMP Google, qui est un domaine différent. Lorsque votre utilisateur clique sur le bouton pour mettre à jour les prix sur votre page, la page AMP mise en cache envoie une requête à votre domaine d'origine pour obtenir les prix, ce qui crée une discordance entre les origines (cache -> domaine d'origine). Pour permettre de telles requêtes d'origine croisée, vous devez gérer CORS, sinon la requête échouera.
D'accord, que dois-je faire ?
- Pour les pages AMP qui récupèrent des données dynamiques, vérifiez que vous testez la version mise en cache de ces pages ; ne vous contentez pas de tester sur votre propre domaine . (Voir la section Tester CORS avec AMP ci-dessous)
- Suivez les instructions de ce document pour gérer les requêtes et réponses CORS.
Utilisation de cookies pour les requêtes CORS
La plupart des composants AMP qui utilisent des requêtes CORS définissent automatiquement le mode des informations d'identification ou permettent à l'auteur de l'activer. Par exemple, le composant amp-list
récupère le contenu dynamique à partir d'un point de terminaison CORS JSON et permet à l'auteur de définir le mode d'authentification via l'attribut données d'authentification
.
Exemple : inclure du contenu personnalisé dans une amp-list via des cookies
<amp-list credentials="include" src="<%host%>/json/product.json?clientId=CLIENT_ID(myCookieId)" > <template type="amp-mustache"> Your personal offer: ${{price}} </template> </amp-list>
En spécifiant le mode d'identification, l'origine peut inclure des cookies dans la requête CORS et également définir des cookies dans la réponse (sous réserve de restrictions de cookies tiers).
Restrictions relatives aux cookies tiers
Les mêmes restrictions de cookies tiers spécifiées dans le navigateur s'appliquent également aux requêtes CORS authentifiées dans AMP. Ces restrictions dépendent du navigateur et de la plateforme, mais pour certains navigateurs, l'origine ne peut définir des cookies que si l'utilisateur a déjà visité l'origine dans une fenêtre tierce (supérieure). Ou, en d'autres termes, seulement après que l'utilisateur ait directement visité le site Web d'origine lui-même. Compte tenu de cela, un service accessible via CORS ne peut pas supposer qu'il sera en mesure de définir des cookies par défaut.
Sécurité CORS avec AMP
Pour garantir des requêtes et des réponses valides et sécurisées pour vos pages AMP, vous devez :
Si vous utilisez Node dans votre backend, vous pouvez utiliser le middleware AMP CORS, qui fait partie de la boîte à outils AMP.
Vérifier les requêtes CORS
Lorsque votre point de terminaison reçoit une requête CORS :
- Vérifiez que l'en-tête CORS
Origin
est une origine autorisée (origine de l'éditeur + caches AMP). - S'il n'y a pas d'en-tête Origin, vérifiez que la requête provient de la même origine (via
AMP-Same-Origin
).
1) Autoriser les requêtes pour des origines CORS spécifiques
Les points de terminaison CORS reçoivent l'origine de la requête via l'en-tête HTTP Origin
. Les terminaux ne devraient autoriser que les requêtes provenant : (1) de la propre origine de l'éditeur ; et (2) de chaque origine cacheDomain
répertoriée dans https://cdn.ampproject.org/caches.json.
Par exemple, les points de terminaison doivent autoriser les requêtes provenant de :
- Sous-domaine de cache AMP Google :
https://<publisher's domain>.cdn.ampproject.org
(par exemple,https://nytimes-com.cdn.ampproject.org
)
2) Autoriser les requêtes de même origine
Pour les requêtes de même origine où l'en-tête Origin
est manquant, AMP définit l'en-tête personnalisé suivant :
AMP-Same-Origin: true
Cet en-tête personnalisé est envoyé par le runtime AMP lorsqu'une requête XHR est effectuée sur la même origine (c'est-à-dire un document diffusé à partir d'une URL ne provenant pas du cache). Autorisez les requêtes qui contiennent l'en-tête AMP-same-origin:true
.
Envoyer des en-têtes de réponse CORS
Après avoir vérifié la requête CORS, la réponse HTTP résultante doit contenir les en-têtes suivants :
Access-Control-Allow-Origin: <origin>
Cet en-tête est une exigence W3 CORS Spec, où origin
fait référence à l’origine de la requête autorisée via l’en-tête de requête CORS Origin
(par exemple, "https://<publisher’s subdomain>.cdn.ampproject.org"
).
Bien que la spécification W3 CORS permette de renvoyer la valeur de *
dans la réponse, pour une sécurité améliorée, vous devez :
- Si l'en-tête
Origin
est présent, valider et transmettre la valeur de l'en-têteOrigin
.
Traitement des requêtes de changement d'état
Avant de traiter des requêtes susceptibles de modifier l'état de votre système (par exemple, un utilisateur s'abonne ou se désabonne d'une liste de diffusion), vérifiez les points suivants :
Si l'en-tête Origin
est défini :
- Si l'origine ne correspond pas à l'une des valeurs suivantes, arrêtez et renvoyez une réponse d'erreur :
<publisher's domain>.cdn.ampproject.org
- l'origine de l'éditeur (c'est-à-dire la vôtre)
où *
représente une correspondance générique et non un astérisque réel (*).
- Sinon, traitez la requête.
Si l'en-tête Origin
n'est PAS défini :
- Vérifiez que la requête contient l'en-tête
AMP-Same-Origin: true
. Si la requête ne contient pas cet en-tête, arrêtez et renvoyez une réponse d'erreur. - Otherwise, process the request.
Exemple de procédure pas à pas : traitement des requêtes et des réponses CORS
Il existe deux scénarios à prendre en compte dans les requêtes CORS adressées à votre point de terminaison :
- Une requête de la même origine.
- Une requête d'une origine mise en cache (à partir d'un cache AMP).
Passons en revue ces scénarios avec un exemple. Dans notre exemple, nous gérons le site example.com
qui héberge une page AMP nommée article-amp.html.
. La page AMP contient une amp-list
pour récupérer des données dynamiques à partir d'un fichier data.json
également hébergé sur example.com
. Nous souhaitons traiter les requêtes de notre fichier data.json
provenant de notre page AMP. Ces requêtes peuvent provenir de la page AMP sur la même origine (non mise en cache) ou de la page AMP sur une origine différente (mise en cache).
Origines autorisées
Sur la base de ce que nous savons sur CORS et AMP (à partir des requêtes Verify CORS ci-dessus), dans notre exemple, nous autoriserons les requêtes provenant des domaines suivants :
example.com
--- Domaine de l'éditeurexample-com.cdn.ampproject.org
--- Sous-domaine du cache AMP Google
En-têtes de réponse pour les requêtes autorisées
Pour les requêtes provenant des origines autorisées, notre réponse contiendra les en-têtes suivants :
Access-Control-Allow-Origin: <origin>
Voici des en-têtes de réponse supplémentaires que nous pourrions inclure dans notre réponse CORS :
Access-Control-Allow-Credentials: true Content-Type: application/json Access-Control-Max-Age: <delta-seconds> Cache-Control: private, no-cache
Logique pseudo CORS
Notre logique de gestion des requêtes et réponses CORS peut être simplifiée dans le pseudo code suivant :
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
Exemple de code CORS
Voici un exemple de fonction JavaScript que nous pourrions utiliser pour gérer les requêtes et réponses CORS :
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); }
Remarque : pour un exemple de code fonctionnel, consultez amp-cors.js.
Scénario 1 : obtenir une requête de la page AMP sur la même origine
Dans le scénario suivant, la page article-amp.html
demande le fichier data.json
; les origines sont les mêmes.
Si nous examinons la requête, nous trouverons :
Request URL: https://example.com/data.json Request Method: GET AMP-Same-Origin: true
Comme cette requête provient de la même origine, il n'y a pas d'en-tête Origin
mais l'en-tête de la requête AMP personnalisé de AMP-Same-Origin: true
est présent. Nous pouvons autoriser cette requête car elle provient de la même origine (https://example.com
).
Nos en-têtes de réponse seraient :
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://example.com
Scénario 2 : obtenir une requête de la page AMP en cache
Dans le scénario suivant, la page article-amp.html
mise en cache dans le cache AMP google demande le fichier data.json
; les origines diffèrent.
Si nous examinons cette requête, nous trouverons :
Request URL: https://example.com/data.json Request Method: GET Origin: https://example-com.cdn.ampproject.org
Comme cette requête contient un en-tête Origin
, nous vérifierons qu'elle provient d'une origine autorisée. Nous pouvons autoriser cette requête car elle provient d'une origine autorisée.
Nos en-têtes de réponse seraient :
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://example-com.cdn.ampproject.org
Utilisation des polices mises en cache
Google AMP Cache met en cache les documents, images et polices AMP HTML pour optimiser la vitesse de la page AMP. Tout en rendant la page AMP rapide, nous voulons également veiller à sécuriser les ressources mises en cache. Nous allons modifier la façon dont le cache AMP répond à ses ressources mises en cache, généralement pour les polices, en respectant la valeur Access-Control-Allow-Origin
.
Comportement passé (avant octobre 2019)
Lorsqu'une page AMP chargeait https://example.com/some/font.ttf
à partir de l'attribut @font-face src
, AMP Cache mettait en cache le fichier de police et diffusait la ressource comme ci-dessous avec l'inconnue Access-Control-Allow-Origin
.
- URL
https://example-com.cdn.ampproject.org/r/s/example.com/some/font.tff
- Access-Control-Allow-Origin : *
Nouveau comportement (octobre 2019 et après)
Bien que l'implémentation actuelle soit permissive, cela pourrait conduire à une utilisation inattendue des polices de sites d'origine croisée. Dans ce changement, AMP Cache commencera à répondre avec exactement la même valeur Access-Control-Allow-Origin
que le serveur d'origine. Pour charger correctement les polices du document AMP mis en cache, vous devrez accepter l'origine AMP Cache via l'en-tête.
Un exemple d'implémentation serait :
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; } }
Par exemple, si vous souhaitez charger /some/font.ttf dans https://example.com/amp.html
, le serveur d'origine doit répondre avec l'en-tête Access-Control-Allow-Origin comme ci-dessous.
Access-Control-Allow-Origin
, le cache AMP fera également écho à cette valeur, ce qui signifie qu'il répondra avec Access-Control-Allow-Origin: *
. Si vous disposez déjà de ce paramètre, il n'est pas nécessaire de modifier quoi que ce soit. Nous prévoyons d'apporter ce changement vers la mi-octobre 2019 et nous attendons de tous les éditeurs AMP utilisant des polices auto-hébergées qu'ils vérifient si cela est affecté.
Plan de déploiement
- 30/09/2019 : la version contient un contrôle plus précis sur les domaines auxquels cette modification s'applique. Cette version devrait être déployée au cours de cette semaine.
- 07/10/2019 : les domaines de test seront activés pour les tests manuels.
- 14/10/2019 : (mais en fonction de la façon dont les tests se déroulent) : la fonctionnalité sera déployée de manière générale.
Suivez le problème associé ici.
Tester CORS dans AMP
Lorsque vous testez vos pages AMP, assurez-vous d'inclure les tests des versions mises en cache de vos pages AMP.
Vérifiez la page via l'URL du cache
Pour vous assurer que votre page AMP mise en cache s'affiche et fonctionne correctement :
- Depuis votre navigateur, ouvrez l'URL que le cache AMP utiliserait pour accéder à votre page AMP. Vous pouvez déterminer le format d'URL de cache à partir de cet outil sur AMP par exemple .
Par exemple :
- URL :
https://amp.dev/documentation/guides-and-tutorials/start/create/
- Format de l'URL AMP Cache :
https://www-ampproject-org.cdn.ampproject.org/c/s/www.ampproject.org/docs/tutorials/create.html
- Ouvrez les outils de développement de votre navigateur et vérifiez qu'il n'y a pas d'erreurs et que toutes les ressources ont été chargées correctement.
Vérifiez les en-têtes de réponse de votre serveur
Vous pouvez utiliser la commande curl
pour vérifier que votre serveur envoie les en-têtes de réponse HTTP corrects. Dans la commande curl
, indiquez l'URL de la requête et tous les en-têtes personnalisés que vous souhaitez ajouter.
Syntaxe : curl <request-url> -H <custom-header> - I
Requête de test de la même origine
Dans une requête de la même origine, le système AMP ajoute l'en-tête personnalisé AMP-Same-Origin:true
.
Voici notre commande curl pour tester une requête de https://ampbyexample.com
vers le fichier examples.json
(sur le même domaine) :
curl 'https://amp.dev/static/samples/json/examples.json' -H 'AMP-Same-Origin: true' -I
Les résultats de la commande affichent les en-têtes de réponse corrects (remarque : les informations supplémentaires ont été coupées) :
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
Requête de test de la page AMP mise en cache
Dans une requête CORS ne provenant pas du même domaine (c'est-à-dire du cache), l'en-tête origin
fait partie de la requête.
Voici notre commande curl pour tester une requête de la page AMP mise en cache sur le cache Google AMP vers le fichier examples.json
:
curl 'https://amp.dev/static/samples/json/examples.json' -H 'origin: https://ampbyexample-com.cdn.ampproject.org' -I
Les résultats de la commande affichent les en-têtes de réponse corrects :
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