DEV Community

Julio Castillo Anselmi
Julio Castillo Anselmi

Posted on

Todo CORS explicado de la A a la Z

Definición de CORS

Antes de definir CORS se definirán algunos conceptos fundamentales para entender CORS.

origen

Dada una URL, el origen es la primera parte que está compuesta por el esquema: el host y el puerto (si está presente).

origen = esquema + host + puerto

Por ejemplo, en https://localhost:8080/encuestas el origen es https://localhost:8080 y /encuesta es el path

mismo-origen (same-origin)

Se refiere al caso en el que cliente hace una petición a un servidor y el servidor tiene el mismo origen que el cliente.

https://aurora.net/home -> https://aurora.net/api/pos?id=290
Enter fullscreen mode Exit fullscreen mode

origen-cruzado (cross-origen)

El cliente hace una petición a un servidor cuyo origen es distinto al del cliente.

https://localhost:8080/home -> https://aurora.net/api/pos?id=290
https://localhost:8080/home -> https://127.0.0.1:8080/api/pos?id=290
https://localhost:8080/home -> https://127.0.0.1:3000/api/pos?id=290
Enter fullscreen mode Exit fullscreen mode

Política de mismo-origen y CORS

Por defecto y por política de seguridad, los navegadores sólo permiten hacer peticiones HTTP al mismo origen, es decir al mismo dominio del de la página que hace la petición.

CORS (Cross-Origin Resource Sharing) es el mecanismo por el cual los navegadores permiten que una petición se haga desde un origen a otro distinto, origen-cruzado.

Para poder realizar una petición CORS el cliente no debe hacer nada, simplemente lanza la petición. El navegador hace la petición y es el servidor el que debe responder si acepta peticiones desde ese origen. Esto lo hace enviando la cabecera (header response) Access-Control-Allow-Origin. Esta cabecera puede tener un asterisco (*) o un origen específico. El * significa que el servidor acepta peticiones de cualquier origen.
El navegador revisa la respuesta del navegador. Si tiene la cabecera Access-Control-Allow-Origin y ésta tiene el valor * o el origen coincide con el origen del cliente, entonces el navegador acepta la respuesta. En caso contrario la descarta y generalmente muestra en consola un error descriptivo de CORS.

 1. navegador con origen X hace petición a origen Y
 2. servidor responde
 3. Si la respuesta del servidor incluye la cabecera `Access-Control-Allow-Origin` Entonces
 4.   Si (Access-Control-Allow-Origin == '*') o (Access-Control-Allow-Origin == X) Entonces
 5.     navegador acepta la respuesta
 6.   Si no
 7.     descarta respuesta y muestra error
 8.   Fin Si
 9. Si no
10.   descarta respuesta y muestra error
11. Fin Si
Enter fullscreen mode Exit fullscreen mode

Access-Control-Allow-Origin también puede tomar el valor null para orígenes desconocidos (como por ejemplo en el caso de acceder desde un fichero en vez de un sitio web). Esto es útil mientras se está en una fase de desarrollo o pruebas, de esta manera no hace falta poner '*' que es menos restrictivo. Access-Control-Allow-Origin: null

Preflight

Arriba se ha visto de manera genérica cómo se debe tratar una petición de origen-cruzado usando CORS. En realidad, antes de hacer una petición de origen-cruzado, los navegadores que implementan CORS harán una "meta" petición previa (preflight) a la petición real.

Un preflight es una petición que hace el navegador antes de efectuar una petición CORS si dicha petición cumple uno de estos criterios:

  • Usa un método HTTP distinto de GET, POST, o HEAD
  • Establece la cabecera Content-Type con un valor distinto de:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • Establece cabeceras adicionales distintas de:
    • Accept
    • Accept-Language
    • Content-Language
  • El objeto XMLHttpRequest contiene eventos upload

A los métodos HTTP GET, POST y HEAD se los denomina métodos simples. Los métodos simples no necesitan ser listados en la cabecera Control-Allow-Methods.

El preflight es una petición con el método HTTP OPTIONS. Esta "meta" petición que hace el navegador sirve para que el servidor decida cómo responder al CORS. En el preflight el navegador envía las cabeceras:

  • Origin
  • Access-Control-Request-Method (admite sólo un método)
  • Access-Control-Request-Headers (sólo si las cabeceras no son simples. Puede tener una lista de cabeceras separada por comas)

El servidor comprueba si el origen y el método HTTP están en su lista de orígenes y métodos permitidos (también comprobará las cabeceras indicadas en Access-Control-Request-Headers, si es que lo hay). Si esta comprobación es favorable responde con un código de status en el rango 200. La respuesta no debería llevar un body. Las cabeceras enviadas por el servidor son:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods (en este caso puede ser una lista de comandos separados por coma)
  • Access-Control-Allow-Headers

Ejemplo de petición preflight:

OPTIONS /api/posts HTTP/1.1
User-Agent: Chrome
Host: 127.0.0.1:8080
Accept: */*
Origin: http://localhost:3000
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Security-Level, App-Name
Enter fullscreen mode Exit fullscreen mode

Ejemplo de respuesta preflight:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http//localhost:3000
Access-Control-Allow-Methods: GET, DELETE
Access-Control-Allow-Headers: Security-Level, App-Name
Enter fullscreen mode Exit fullscreen mode

Ejemplo de petición:

GET /api/posts HTTP/1.1
User-Agent: Chrome
Host: 127.0.0.1:8080
Accept: */*
Origin: http://localhost:3000
Security-Level: High
App-Name: Encuesta
Enter fullscreen mode Exit fullscreen mode

El algoritmo anterior, ahora modificado con los preflight quedaría así:

 1. el cliente Javascript cuyo origen es X, hace una petición P al servidor con origen Y.
       P contiene el método HTTP M y un conjunto de cabeceras C
 2. Si M es un método HTTP simple y todas las cabeceras de C son simples Entonces
 3.   enviar la petición P al servidor
 4. Si no
 5.   preparar una petición preflight F con método HTTP OPTIONS y un conjunto de cabeceras C' con:
        Origin = X, Access-Control-Request-Method = M
 6.   Si en C hay cabeceras que no son simples Entonces
 7.     agregar a C' la cabecera Access-Control-Request-Headers con el listado de cabeceras no simples de C
 8.   Fin Si
 9.   enviar preflight F al servidor
10    Si servidor responde el preflight con status en el rango de 200 Entonces
11.     enviar la petición P al servidor
12.   Si no
13.     comprobar las cabeceras de respuesta Access-Control-Allow-Origin,
          Access-Control-Allow-Methods y Access-Control-Allow-Headers para determinar cuáles
          son las validaciones que han fallado e informar el error
14.   Fin Si
15. Fin Si
Enter fullscreen mode Exit fullscreen mode

Cache. El navegador puede guardar en caché los preflights y así evitar hacer una petición extra por cada petición del cliente que requeriría preflight. La caché guarda en sus entradas origen + path.

Canvas e imágenes

En el caso de imágenes cuyo atributo src es un origen distinto al del cliente, el navegador carga el recurso y lo muestra pero no permite hacer manipulaciones con ella como toBlob, toDataURL y getImageData.
Para habilitar el CORS en el caso de imágenes, existe el atributo crossOrigin que puede tomar los valores anonymous o user-credentials. user-credentials es como el withCredentials de XMLHttpRequest, es decir que las credenciales de usuario (por ejemplo, cookies) se envían con la petición. Si no es necesario enviar las credenciales, es preferible enviar anonymous.

Cookies y credenciales de usuario

Por defecto el navegador no envía las credenciales de usuario como las cookies en las peticiones de origen-cruzado. El navegador tampoco expone todos las cabeceras de respuesta que envía el servidor.
Por eso CORS tiene mecanismos para remediar esas restricciones: las cabeceras de respuesta Access-Control-Allow-Credentials y Access-Control-Expose-Headers.

Entonces para que el uso de cookies es necesario que el cliente indique:

xhr.withCredentials = true;
document.cookie = someUniqueId;
Enter fullscreen mode Exit fullscreen mode

El servidor debe responder al preflight y a la petición con las cabeceras
Access-Control-Allow-Credentials y Access-Control-Allow-Origin.

Cuando servidor envía la cabecera Access-Control-Allow-Credentials con el valor true, debe enviar la cabecera Access-Control-Allow-Origin con un origen específico; Access-Control-Allow-Origin: * es inválido porque aceptar cookies de cualquier origen puede generar problemas de seguridad.

Válido:

Access-Control-Allow-Origin: http://localhost:1111
Access-Control-Allow-Credentials: true
Enter fullscreen mode Exit fullscreen mode

Inválido:

Access-Control-Allow-Origin: \*
Access-Control-Allow-Credentials: true
Enter fullscreen mode Exit fullscreen mode

En el caso en el que el cliente tiene xhr.withCredentials = true y el servidor responde con Access-Control-Allow-Origin: true, el servidor puede establecer cookies en el cliente. Estas cookies serán enviadas por el navegador en las peticiones sucesivas, sin embargo, el client no podrá leer dichas cookies con Javascript.

Cabeceras de respuesta

El objeto XMLHttpRequest expone dos métodos para leer las cabeceras de respuesta: getResponseHeader y getAllResponseHeaders. En una petición mismo-origen estos métodos devuelven todas las cabeceras enviadas por el servidor. Cuando se trata de una petición de origen-cruzado, sólo se pueden leer las cabeceras simples.

Usando la cabecera Access-Control-Expose-Headers el servidor puede especificar qué cabeceras puede leer el cliente.

Access-Control-Expose-Headers: X-Powered-By
Enter fullscreen mode Exit fullscreen mode

Cabeceras simples:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

Resumen de cabeceras usadas en CORS

Cabeceras de petición (enviadas por el navegador cliente)

  • Origin: Cabecera que añade el navegador. Contiene el origen del cliente en la petición. Contraparte: Access-Control-Allow-Origin.
  • Access-Control-Request-Method: Cabecera que añade el navegador en el preflight. Contiene el método HTTP que se hará el cliente en la petición real. Contraparte: Access-Control-Allow-Methods.
  • Access-Control-Request-Headers: Cabecera que añade el navegador en el preflight. Contiene las cabeceras HTTP que enviará el cliente en la petición real. Contraparte: Access-Control-Allow-Headers.

Cabeceras de respuesta (enviadas por el servidor)

  • Access-Control-Allow-Origin: El valor * indica que se permiten todos los orígenes
    Un valor específico indica que el servidor sólo acepta peticiones de ese origen
    El valor null indica que sólo acepta peticiones de orígenes que no son un sitio web, por ejemplo un fichero (sólo recomendado para desarrollo y pruebas). Contraparte: Origin

  • Access-Control-Allow-Credentials: El valor true indica que el servidor soporta el uso de credenciales de usuario tales como las cookies.

  • Access-Control-Allow-Methods: Tiene una lista de métodos HTTP aceptados por el servidor.
    No es necesario incluir los métodos simples, aunque es buena práctica hacerlo.
    Esta cabecera sólo debe enviarse en respuesta a peticiones preflight. Contraparte: Access-Control-Request-Methods

  • Access-Control-Allow-Headers: Tiene una lista de cabeceras HTTP aceptadas por el servidor.
    Esta cabecera sólo debe enviarse en respuesta a peticiones preflight. Contraparte:Access-Control-Request-Headers

  • Access-Control-Max-Age: Su valor especifica el tiempo máximo en segundos que se recomienda mantener en caché un preflight, aunque el navegador puede tener su propio valor máximo.
    Esta cabecera sólo debe enviarse en respuesta a peticiones preflight.

  • Access-Control-Expose-Headers: Contiene las cabeceras que pueden ser leídos por el navegador. Puede tener una lista de cabeceras separada por comas.
    Es opcional y no es necesario para que la petición CORS sea exitosa.

Top comments (4)

Collapse
 
iban_ferrobaales_cacd27 profile image
Iban Ferro Bañales

Qué ocurre si en la solicitud Preflight, en lugar de recibir un 200 OK como respuesta, se recibe algún otro código de error HTTP como 405 u otros? Acaso el navegador llevará a cabo la solicitud CORS?

Collapse
 
julcasans profile image
Julio Castillo Anselmi

El navegador espera recibir un 200 como respuesta para proseguir con la solicitud. Cualquier otro código devuelto por el servidor significa que el preflight ha fallado (por no estar permitido o incluso por un error).

Collapse
 
lopez089 profile image
Juan López Aragón

Muy buen artículo. Gran trabajo. Muchas gracias

Collapse
 
jpgonzalezquinteros profile image
Juan Pablo Gonzalez Quinteros

Excelente explicacion muy completa!