Conozca la diferencia entre solicitudes HTTP simples y XHR.
Aplicación web de origen: https://github.com
Cuando abre un sitio web escribiendo Origin Web App, su navegador realiza solicitudes HTTP simples. CORS no es aplicable aquí. Ya que solicitamos voluntariamente ofrecer contenido de Origin Web App. Su navegador lo/debería servirlo sin ningún problema. Pero, cuando se trata de solicitudes XHR, el JavaScript que carga la aplicación web Origin en su navegador puede realizar una solicitud a cualquier servidor (https://netflix.com) que solicite un recurso. Ahora, Origin Web App es propiedad de Github, pero https://netflix.com es propiedad de Netflix, ese servidor potencialmente podría servir cualquier cosa. Github no puede tomar el control de un servidor propiedad de Netflix. Esto tiene muchas implicaciones de seguridad, potencialmente robar contenido de un sitio web (podría ser un sitio web financiero) a cualquier servidor remoto.
Afortunadamente, CORS aborda muy bien este problema con un conjunto de reglas determinado.
Es un estándar definido por el W3C para permitir que las solicitudes de origen cruzado entre el cliente (navegador) y el servidor compartan recursos al mismo tiempo que mantienen la seguridad. Cualquier navegador cumplirá con estos estándares para evitar la carga de recursos desde servidores de terceros.
Un encabezado indica de dónde se origina una solicitud. Se envía con solicitudes CORS, así como con solicitudes POST.
Sintaxis:
Origin: <scheme> "://" <hostname> [":" <port> ]
Ejemplos:
Origin: https://netflix.com
Origin: http://netflix.com:443
Origin: http://localhost:1443
El navegador utiliza este encabezado al emitir solicitudes previas al vuelo para indicar qué método de solicitud se utilizará cuando se realice la solicitud real.
Sintaxis:
Access-Control-Request-Method: <method>
<method>
podría ser GET
, POST
o DELETE
.
Ejemplo:
Access-Control-Request-Method: POST
Este encabezado se utiliza nuevamente en las solicitudes previas al vuelo del navegador para indicar qué encabezados de solicitud se utilizarán cuando se realice la solicitud real. Para utilizar varios encabezados, deben estar separados por comas.
Sintaxis:
Access-Control-Request-Headers: <header-name>
Access-Control-Request-Headers: <header-name>, <header-name>
Ejemplo:
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
Importante: Todo el encabezado debe estar en la lista segura de CORS o ser un encabezado personalizado como X-Custom-Header
.
Accept
Accept-Language
Content-Language
Content-Type
Los valores permitidos son application/x-www-form-urlencoded
, multipart/form-data
y text/plain
.
Los siguientes encabezados se devuelven en las solicitudes previas al vuelo.
Este encabezado indica si el Origen solicitado está permitido. Su navegador elegirá si la solicitud tiene éxito/falla haciendo coincidir el origen solicitado con esto.
Sintaxis:
Access-Control-Allow-Origin: *
Para solicitudes sin credenciales, el valor *
se puede especificar como comodín. Esto le indica a su navegador que permita solicitudes de cualquier origen.
Access-Control-Allow-Origin: <origin>
Cuando recibe solo un origen en el encabezado de respuesta, significa que su servidor/aplicación web, según el Origin
solicitado, responde con el mismo Origin
si está permitido. Su servidor también debe responder con Vary para indicar que varía según el encabezado de una solicitud.
Ejemplo:
Access-Control-Allow-Origin: https://github.com
Su navegador realizará una solicitud real si uno de los valores de este encabezado coincide. Cuando se devuelve el comodín *
, significa que se permite cualquier método.
Sintaxis:
Access-Control-Allow-Methods: <method>, <method>, ...
Access-Control-Allow-Methods: *
Ejemplo:
Access-Control-Allow-Methods: GET, POST
Su navegador realizará una solicitud real si todos los encabezados solicitados están permitidos.
Sintaxis:
Access-Control-Allow-Headers: <header-name>, <header-name>
Access-Control-Allow-Headers: *
Ejemplo:
Access-Control-Allow-Headers: Accept, Content-Type
El comodín *
le indica al navegador que se permite cualquier encabezado.
Indica durante cuánto tiempo se pueden almacenar en caché los resultados del vuelo previo.
Access-Control-Max-Age: <delta-seconds>
nº máximo de segundos los resultados se pueden almacenar en caché.
Cada navegador tiene un límite máximo,
Varíe el encabezado utilizado en general para fines de almacenamiento en caché para determinar si se puede usar una respuesta almacenada en caché o si debe volver a almacenarse en caché según el valor del encabezado.
En CORS, digamos que el servidor permite múltiples orígenes según el Origin
solicitado, devolverá una URL particular en Access-Control-Allow-Origin
.
Sintaxis:
Vary: <header-name>
Ejemplo:
Digamos que github.com permite que tanto https://github.com como https://netflix.com soliciten recursos. Considere los siguientes escenarios,
Escenario 1:
curl -X OPCIONES https://github.com/api/v1/gists/1
Origin: https://github.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type
Access-Control-Allow-Origin: https://github.com
Vary: Origin
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 600
Ahora, en este escenario, el navegador almacenará en caché los resultados de esta solicitud previa al vuelo durante 10 minutos.
Escenario 2:
curl -X OPCIONES https://github.com/api/v1/gists/1
Origin: https://netflix.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type
Access-Control-Allow-Origin: https://netflix.com
Vary: Origin
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 300
Ahora puedes notar que Access-Control-Allow-Origin
contiene https://netflix.com, este es un ejemplo de cómo la respuesta varía según Origin
determinado. También lo hace la edad máxima de esta respuesta, que se almacena en caché durante solo 5 minutos, a diferencia del primer escenario.
Algunas solicitudes no activan una solicitud previa al vuelo para CORS. A esto lo llamamos solicitudes simples . Debe cumplir las siguientes condiciones:
Uno de los métodos permitidos:
Además de los encabezados que el agente de usuario realiza automáticamente (por ejemplo, Connection
o User-Agent
), los únicos encabezados que se pueden configurar manualmente son los encabezados de solicitud incluidos en la lista segura de CORS y los siguientes:
DPR
Downlink
Save-Data
Viewport-Width
Width
No se registran detectores de eventos en ningún objeto XMLHttpRequestUpload
utilizado en la solicitud.
No se utiliza ningún objeto ReadableStream
en la solicitud.
Ejemplo:
Pedido:
GET /api/v1/public-data/ HTTP/1.1
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
Encabezados de respuesta:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
Cuando una solicitud devuelve *
en el encabezado Access-Control-Allow-Origin
. Significa que se puede realizar una solicitud desde cualquier Host
. En este caso, su navegador no realizará una solicitud previa al vuelo.
Su navegador realizará una solicitud previa al vuelo para determinar si es seguro enviar la solicitud real.
Ejemplo:
const xhr = new XMLHttpRequest ( ) ;
xhr . open ( 'POST' , 'https://bar.com/api/v1/post-here/' ) ;
xhr . setRequestHeader ( 'X-PINGOTHER' , 'pingpong' ) ;
xhr . setRequestHeader ( 'Content-Type' , 'application/json;charset=UTF-8' ) ;
xhr . onreadystatechange = handler ;
xhr . send ( JSON . stringify ( { "email" : "[email protected]" } ) ) ;
Solicitud previa al vuelo:
OPTIONS /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type, Accept
Solicitud real:
POST -d '{foo: bar}' /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
X-PINGOTHER: pingpong
Content-Type: application/json;charset=UTF-8
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
Cuando se configura un encabezado no estándar como X-PINGOTHER
, su navegador no sabrá si es seguro realizar la solicitud. Para asegurarse de que sea seguro, su navegador realiza una solicitud de OPCIONES con Access-Control-Request-Headers
que contienen X-PINGOTHER
y Content-Type
. Al validar con los encabezados de respuesta de la solicitud previa al vuelo, su navegador realiza la solicitud real.
En general, cuando realiza una solicitud XHR, las cookies no se transmiten junto con la solicitud. Cuando sea necesario pasar cookies, deberá establecer una marca en el objeto XMLHttpRequest
.
const xhr = new XMLHttpRequest ( ) ;
const url = 'http://bar.com/api/v1/credentialed-content/' ;
function callOtherDomain ( ) {
if ( invocation ) {
xhr . open ( 'GET' , url , true ) ;
xhr . withCredentials = true ;
xhr . onreadystatechange = handler ;
xhr . send ( ) ;
}
}
Pedido:
POST -d '{foo: bar}' /api/v1/post-here/
Host: bar.com
User-Agent: curl/7.69.1
Accept: */*
Origin: https://foo.com
X-PINGOTHER: pingpong
Content-Type: application/json;charset=UTF-8
Cookie: _session=NyV14oKXiS6HHopaf9nT
Cuando se realiza una solicitud XMLHttpRequest
y se establece el indicador withCredentials
, su navegador transmitirá la Cookie
en el encabezado de la solicitud.
Respuesta:
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Type: application/json
Access-Control-Allow-Origin: https://foo.com
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: _session=AjBSqxj4T7bSySNTWeEm; expires=Wed, 31-05-2020 00:00:00 GMT
Cuando su navegador nota Access-Control-Allow-Credentials
está configurado en verdadero. Respetará el encabezado Set-Cookie
y establecerá la cookie.
Importante: El comodín "*" no debe establecerse en Access-Control-Allow-Origin
como se menciona en la sección Solicitudes con credenciales y comodines.
Cuando se realiza una solicitud de credenciales configurando el indicador withCredentials
, el servidor debe configurar Access-Control-Expose-Headers
para que el navegador sepa a qué encabezados se puede acceder.
En el mundo anterior a Cors, el navegador no puede acceder a los encabezados de respuesta predeterminados en la solicitud CORS. Por lo tanto, se hace explícito para que el navegador busque este encabezado para leer los encabezados expuestos. De esta manera, la especificación CORS garantiza que los navegadores antiguos no fallen.
Esto se devuelve en las solicitudes previas al vuelo. Cuando su navegador vea esto, podrá acceder al encabezado Set-Cookie
. Como mencionamos anteriormente, en solicitudes XHR normales, su navegador no pasará Cookie
en el encabezado de la solicitud ni leerá el encabezado de respuesta Set-Cookie
.
Sintaxis:
Access-Control-Allow-Credentials: true
Puede encontrar un ejemplo en la sección Solicitud con credenciales.
Cuando decimos solicitud acreditada, significa que las cookies se pasan en la solicitud XHR o se configuran mediante el encabezado de respuesta Set-Cookie
. Cuando se realiza una solicitud XHR con el indicador withCredentials, espera recibir una respuesta junto con las cookies que se configurarán. Pero no puede esperar Access-Control-Allow-Origin
sea "*" porque eso significaría que cualquier sitio web puede utilizar estas cookies. Por este motivo, su navegador fallará la solicitud si ve "*" en el encabezado de respuesta Access-Control-Allow-Origin
.
Cuando una solicitud previa al vuelo responde con 301/302, es posible que algunos navegadores no admitan esto actualmente. Es posible que obtenga errores como,
The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight
Request requires preflight, which is disallowed to follow cross-origin redirect
Nota: Para encontrar soluciones alternativas, consulte Solicitudes verificadas previamente y documentos de redireccionamiento de Mozilla.
El navegador tiene configuraciones para rechazar todas las cookies third-party
, cuando un usuario lo habilita. Por ejemplo, si se realiza una solicitud desde https://foo.com
y el servidor está en https://bar.com
, su navegador no configurará las cookies enviadas por https://bar.com
.