- ¿Por qué necesito CORS para mi propio origen?
- Cómo utilizar las cookies en las solicitudes CORS
- La seguridad de CORS en AMP
- Ejemplo paso a paso: cómo administrar solicitudes y respuestas CORS
- Los orígenes que están permitidos
- Los encabezados de respuesta para las solicitudes que están permitidas
- La pseudológica de CORS
- Escenario 1: cómo recibir solicitudes desde la página de AMP que provengan del mismo origen
- Escenario 2: cómo recibir solicitudes desde una página que se almacenó en el caché de AMP
- Cómo trabajar con fuentes que se almacenaron en el caché
- Cómo probar CORS en AMP
CORS en AMP
Muchos de los componentes y extensiones de AMP aprovechan los endpoints remotos mediante el uso de solicitudes para el Intercambio de recursos de origen cruzado (CORS). En este documento se explican los aspectos más importantes sobre el uso de CORS en AMP. Para obtener más información sobre CORS, consulte la especificación W3 de CORS.
- ¿Por qué necesito CORS para mi propio origen?
- Cómo utilizar las cookies en las solicitudes CORS
-
¿Por qué necesito CORS para mi propio origen?
Posiblemente esté confundido sobre por qué necesitaría CORS para administrar las solicitudes que llegan a su propio origen, a continuación, profundizaremos en eso.
Los componentes de AMP que obtienen datos dinámicos (por ejemplo, amp-form, amp-list, etc.) llevan a cabo solicitudes CORS en endpoints remotos para recuperar los datos. Si su página AMP incluye dichos componentes, deberá habilitar CORS para que esas solicitudes no fallen.
Representemos esto con un ejemplo:
Supongamos que tiene una página AMP en la que se registran productos con precios. Para actualizar los precios en la página, el usuario hace clic en un botón que recupera los precios más recientes desde un endpoint JSON (lo realiza a través del componente amp-list). El JSON se encuentra en su dominio.
Bien, entonces si la página se encuentra en mi dominio y JSON también está en mi dominio. ¡No veo ningún problema!
Vaya, pero ¿cómo llegó el usuario a su página AMP? ¿accede desde una página que está almacenada en el caché? Es muy probable que su usuario no haya accedido a su página AMP directamente, sino que haya descubierto su página mediante otra plataforma. Por ejemplo, Google Search utiliza el caché AMP de Google para renderizar rápidamente las páginas AMP. Estas son páginas almacenadas en el caché y proporcionan sus servicios desde el caché AMP de Google, que es un dominio diferente. Cuando su usuario hace clic en el botón para actualizar los precios en su página, la página que está almacenada en el caché de AMP envía una solicitud a su dominio de origen para obtener los precios, lo cual genera una discrepancia entre los orígenes (caché -> dominio de origen). Para permitir tales solicitudes de origen cruzado es necesario que habilite CORS, de lo contrario las solicitudes fallarán.
Muy bien, ¿qué debo hacer ahora?
- Para las Páginas AMP que obtienen datos dinámicos, asegúrese de probar la versión en caché de esas páginas, no se restrinja a probarlas solo en su propio dominio. (Consulte la sección Cómo probar CORS en AMP que se encuentra más adelante).
- Siga las instrucciones que se encuentran en este documento para habilitar las solicitudes y respuestas de CORS.
Cómo utilizar las cookies en las solicitudes CORS
En la mayoría de los componentes de AMP que utilizan las solicitudes CORS se configura automáticamente el modo de credenciales o permiten que el autor lo habilite de forma opcional. Por ejemplo, el componente amp-list
obtiene contenido dinámico desde un endpoint JSON mediante CORS y permite que autor establezca el modo de credenciales mediante el atributo credentials
.
Ejemplo: Cómo incorporar contenido personalizado en un componente amp-list utilizando 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 cuanto especifique el modo credenciales, el origen puede incluir cookies en la solicitud CORS y también establecer cookies para la respuesta (sujeto a las restricciones de las cookies de terceros).
Restricciones de las cookies de terceros
Las mismas restricciones de las cookies de terceros que se especifican en el navegador también se aplican a las solicitudes CORS acreditadas en AMP. Estas restricciones dependen del navegador y de la plataforma, pero para algunos navegadores, solo pueden establecerse cookies si el usuario visitó previamente la primera parte de una ventana (la parte superior) del origen. O dicho de otra manera, solo después de que el usuario haya visitado directamente el sitio web de origen. Teniendo en cuenta esto, si accede a un servicio a través de CORS no puede suponer que podrán establecerse las cookies de forma predeterminada.
La seguridad de CORS en AMP
Con el fin de verificar que las solicitudes y respuestas sean válidas y seguras para sus páginas AMP, debe:
Si está usando Node en su backend, puede usar el middleware AMP mediante CORS, que es parte del conjunto de herramientas de AMP.
Cómo verificar las solicitudes CORS
Cuando su endpoint reciba una solicitud CORS, realice lo siguiente:
- Compruebe que el encabezado
Origin
de CORS provenga de un origen permitido (el origen del editor + los cachés de AMP). - Si no hay un encabezado Origin, verifique que la solicitud provenga del mismo origen (mediante
AMP-Same-Origin
).
1) Cómo autorizar las solicitudes CORS de orígenes específicos
Los endpoints de CORS reciben la solicitud del origen a través del encabezado HTTP Origin
. Los endpoints solo deben permitir solicitudes que provengan de: (1) el propio origen del editor y de (2) cada cacheDomain
con un origen registrado en https://cdn.ampproject.org/caches.json.
Por ejemplo, los endpoints deben permitir solicitudes que provengan de:
- Un subdominio del caché AMP de Google:
https://<publisher's domain>.cdn.ampproject.org
(por ejemplo,https://nytimes-com.cdn.ampproject.org
)
2) Cómo autorizar las solicitudes que provengan del mismo origen
Para las solicitudes que provengan del mismo origen dónde falte el encabezado Origin
, AMP establece el siguiente encabezado personalizado:
AMP-Same-Origin: true
El tiempo de ejecución de AMP envía este encabezado personalizado cuando se realiza una solicitud XHR desde el mismo origen (es decir, un documento que presta servicios desde una URL sin caché). Permite solicitudes que contengan el encabezado AMP-Same-Origin:true
.
Cómo enviar la respuesta a los encabezados CORS
Después de verificar la solicitud CORS, la respuesta que se origine de HTTP debe contener los siguientes encabezados:
El encabezado de respuesta Access-Control-Allow-Origin: <origin>
Este encabezado es un requisito de la especificación W3 de CORS donde origin
se refiere al origen de la solicitud que se autorizó mediante el encabezado de la solicitud CORS Origin
(por ejemplo, "https://<publisher's subdomain>.cdn.ampproject.org"
).
A pesar de que la especificación W3 de CORS permite devolver el valor de *
en la respuesta, para mejorar la seguridad, debe hacer lo siguiente:
- Si el encabezado
Origin
está presente, compruebe y repita la validación del encabezadoOrigin
.
Cómo procesar las solicitudes de cambio de estado
Antes de procesar solicitudes que podrían cambiar el estado de su sistema (por ejemplo, cuando un usuario se suscribe o cancela su suscripción de una lista de correos), verifique lo siguiente:
Si contiene el encabezado Origin
:
- Si el origen no coincide con alguno de los siguientes valores, detenga el proceso y devuelva una respuesta de error:
<publisher's domain>.cdn.ampproject.org
- el origen del editor (alias “el suyo”)
donde *
corresponde a un comodín y no es un asterisco real (*).
- De lo contrario, procese la solicitud.
Si NO contiene el encabezado Origin
:
- Verifique que la solicitud contenga el encabezado
AMP-Same-Origin: true
. Si la solicitud no contiene este encabezado, detenga el proceso y devuelva una respuesta de error. - De lo contrario, procese la solicitud.
Ejemplo paso a paso: cómo administrar solicitudes y respuestas CORS
Debe tener en cuenta dos escenarios para las solicitudes CORS que recibe su endpoint:
- Una solicitud que provenga del mismo origen.
- Una solicitud proveniente un origen que se almacene en el caché (desde un caché de AMP).
Analicemos estos escenarios con un ejemplo. En nuestro ejemplo, administramos el sitio example.com
que aloja una página AMP llamada article-amp.html.
La página AMP contiene un amp-list
para recuperar datos dinámicos de un archivo data.json
que también está alojado en example.com
. Si deseamos procesar las solicitudes en nuestro archivo data.json
que provengan de nuestra página AMP. Estas solicitudes podrían tener el mismo origen que la página AMP (la cual no está almacenada en el caché) o provenir de la página AMP (que está almacenada en el caché) pero tiene un origen diferente.
Los orígenes que están permitidos
Basándonos en lo que sabemos sobre CORS y AMP (desde la sección Verificar las solicitudes CORS que se encuentra más arriba), en nuestro ejemplo aceptaremos las solicitudes que provengan de los siguientes dominios:
example.com
--- Provenientes del dominio del editorexample-com.cdn.ampproject.org
--- Provenientes del subdominio para el caché AMP de Google
Los encabezados de respuesta para las solicitudes que están permitidas
En las solicitudes que provengan de orígenes permitidos, nuestra respuesta deberá incluir los siguientes encabezados:
Access-Control-Allow-Origin: <origin>
Estos son encabezados de respuesta adicionales que podríamos incluir en nuestra respuesta CORS:
Access-Control-Allow-Credentials: true Content-Type: application/json Access-Control-Max-Age: <delta-seconds> Cache-Control: private, no-cache
La pseudológica de CORS
Nuestra lógica para administrar las solicitudes y respuestas de CORS puede simplificarse en el siguiente pseudocódigo:
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
Ejemplo del código que se utiliza en CORS
Este es el ejemplo de una función en JavaScript que podríamos utilizar para habilitar las solicitudes y respuestas de 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); }
Nota: Si desea un ejemplo funcional del código, consulte amp-cors.js.
Escenario 1: cómo recibir solicitudes desde la página de AMP que provengan del mismo origen
En el siguiente escenario, la página article-amp.html
solicita el archivo data.json
, cuyos orígenes son los mismos.
Si analizamos esta solicitud, encontraremos:
Request URL: https://example.com/data.json Request Method: GET AMP-Same-Origin: true
Como esta solicitud proviene del mismo origen, no hay un encabezado Origin
, pero el encabezado personalizado de la solicitud AMP de AMP-Same-Origin: true
está presente. Permitiremos esta solicitud ya que proviene del mismo origen (https://example.com
).
Nuestros encabezados de respuesta serían los siguientes:
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://example.com
Escenario 2: cómo recibir solicitudes desde una página que se almacenó en el caché de AMP
En el siguiente escenario, la página article-amp.html
que se almacenó en el caché AMP de Google solicita el archivo data.json
, en este caso los orígenes son diferentes.
Si analizamos esta solicitud, encontraremos:
Request URL: https://example.com/data.json Request Method: GET Origin: https://example-com.cdn.ampproject.org
Como esta solicitud contiene un encabezado Origin
, verificaremos que provenga de un origen permitido. Debido a que esta solicitud proviene de un origen permitido, entonces podemos autorizarla.
Nuestros encabezados de respuesta serían los siguientes:
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://example-com.cdn.ampproject.org
Cómo trabajar con fuentes que se almacenaron en el caché
En el caché AMP de Google se almacenan documentos, imágenes y fuentes AMP HTML con la finalidad de optimizar la velocidad de la página de AMP. Conforme esto hace que la página AMP sea más rápida, también deseamos que proteja los recursos que se almacenen en el caché. Para ello, realizaremos un cambio en la forma que el caché de AMP responde ante los recursos que se almacenaron en el caché, aunque generalmente para las fuentes se respeta el valor del origen Access-Control-Allow-Origin
.
Comportamiento anterior (antes de octubre del 2019)
Cuando una página de AMP estaba cargando https://example.com/some/font.ttf
desde el atributo @font-face src
, el caché de AMP almacena temporalmente en el caché la fuente del archivo y funcionará como un recurso sin la necesidad de tener el comodín Access-Control-Allow-Origin
, como se muestra a continuación:
- URL
https://example-com.cdn.ampproject.org/r/s/example.com/some/font.tff
- Access-Control-Allow-Origin: *
Comportamiento nuevo (a partir de octubre del 2019)
Mientras la implementación actual sea laxa podría conducir a un uso imprevisto de las fuentes que provienen de sitios de origen cruzado. En este cambio, el caché de AMP comenzará a responder exactamente con el mismo valor de Access-Control-Allow-Origin
que el servidor de origen. Para cargar correctamente las fuentes desde el documento que está almacenado en el caché de AMP, deberá aceptar el origen del caché de AMP mediante el encabezado.
Un ejemplo de la implementación sería el siguiente:
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; } }
Por ejemplo, si desea cargar /some/font.ttf en https://example.com/amp.html
, el servidor de origen debe responder con el encabezado Access-Control-Allow-Origin, como se muestra a continuación.
Access-Control-Allow-Origin
, el caché de AMP también repetirá la validación, lo cual significa que responderá con Access-Control-Allow-Origin: *
. Si ya tiene esta configuración, no es necesario cambiar nada. Planeamos realizar este cambio a mediados de octubre del 2019 y esperamos que todos los editores de AMP que utilizan fuentes de alojamiento propio verifiquen si esto les afecta.
Plan de implementación
- 30-09-2019: la versión del lanzamiento contiene un control más preciso sobre cuáles son los dominios en los que se aplica este cambio. Esa compilación debería implementarse en el transcurso de esta semana.
- 07-10-2019: los dominios de prueba se habilitarán para las pruebas manuales.
- 14-10-2019: (pero dependiendo de cuáles hayan sido los resultados de las pruebas): la función se implementará de manera general.
Siga todas las novedades sobre este tema aquí.
Cómo probar CORS en AMP
Cuando esté probando sus páginas de AMP, asegúrese de incluir pruebas de las versiones que se hayan almacenado en el caché.
Cómo verificar la página mediante la URL que se almacenó en el caché
Para garantizar que la página almacenada en el caché de AMP se renderice y funcione correctamente, siga los siguientes pasos:
- Desde su navegador, abra la URL que el caché de AMP utilizaría para acceder a su página AMP. Puede establecer el formato que tendrá la URL en el caché con la herramienta AMP By Example.
Por ejemplo:
- URL:
https://amp.dev/documentation/guides-and-tutorials/start/create/
- Formato de URL para el caché de AMP:
https://www-ampproject-org.cdn.ampproject.org/c/s/www.ampproject.org/docs/tutorials/create.html
- Abra las herramientas de desarrollo para su navegador, verifique que no haya errores y que todos los recursos se hayan cargado correctamente.
Cómo verificar los encabezados de respuesta en su servidor
Puede utilizar el comando curl
para verificar que su servidor envíe los encabezados de respuesta HTTP correctos. En el comando curl
, proporcione la solicitud de la URL y los encabezados personalizados que desee agregar.
Sintaxis: curl <request-url> -H <custom-header> - I
Cómo probar la solicitud desde el mismo origen
En una solicitud con el mismo origen, el sistema de AMP agrega el encabezado personalizado AMP-Same-Origin:true
.
Aquí encontrará nuestro comando curl para probar una solicitud que provenga de https://ampbyexample.com
en el archivo examples.json
(en el mismo dominio):
curl 'https://amp.dev/static/samples/json/examples.json' -H 'AMP-Same-Origin: true' -I
En los resultados del comando se muestran los encabezados de respuesta correctos (nota: se omitió la información adicional):
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
Cómo probar la solicitud de la página AMP almacenada en el caché
En una solicitud CORS que no proviene del mismo dominio (es decir, del caché), el encabezado origin
es parte de la solicitud.
Aquí encontrará nuestro comando curl para probar una solicitud que proviene desde la página AMP, la cual está almacenada en el caché AMP de Google en el archivo examples.json
:
curl 'https://amp.dev/static/samples/json/examples.json' -H 'origin: https://ampbyexample-com.cdn.ampproject.org' -I
En los resultados del comando se muestran los encabezados de respuesta correctos:
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