Seguridad web

CORS (Cross-Origin Resource Sharing)

Se lee en aproximadamente 4 minutos

Qué es CORS (Cross-Origin Resource Sharing)

CORS (Cross-Origin Resource Sharing) es un mecanismo basado en encabezados HTTP que relaja de forma segura la política del mismo origen del navegador web y permite compartir recursos entre orígenes distintos. Un origen se define por la combinación de tres elementos: el esquema (http/https), el nombre de host y el número de puerto.

La política del mismo origen es el mecanismo de seguridad del navegador que restringe que un script cargado desde un origen lea la respuesta de otro origen. Sin esa restricción, un sitio malicioso podría llamar, a través del navegador del usuario, a la API del sitio bancario en el que este tiene la sesión abierta y leer con su propio script el saldo y el historial de operaciones incluidos en la respuesta.

Conviene tener presente esta relación: lo que protege de forma predeterminada es la política del mismo origen, y CORS es el mecanismo para relajar esa restricción de forma selectiva. Entenderlo como "configurar CORS para impedir los ataques entre orígenes" invierte el sentido; en realidad equivale a abrir, solo en la medida necesaria, algo que está cerrado por defecto. Un punto más: lo que la política del mismo origen detiene es la lectura de la respuesta, no el envío de la solicitud. Que el envío sí se complete es la razón por la que CSRF sigue siendo una amenaza real.

Sin embargo, en las aplicaciones web actuales es habitual que el frontend y la API del backend se operen en dominios distintos. CORS hace posible de forma segura la comunicación entre orígenes que se necesita, porque el servidor declara de manera explícita en los encabezados de respuesta HTTP desde qué orígenes permite el acceso.

Mecanismo de funcionamiento de CORS

CORS funciona de dos maneras según el tipo de solicitud.

Solicitud simple (Simple Request)

Cuando el método es GET, HEAD o POST, los encabezados añadidos se mantienen dentro del alcance de la lista segura (Accept, Accept-Language, Content-Language y Content-Type) y el valor de Content-Type es uno de application/x-www-form-urlencoded, multipart/form-data o text/plain, la solicitud se trata como "solicitud simple". Visto al revés, basta añadir un solo encabezado propio como Authorization o X-Requested-With para salir de esa condición.

En una solicitud simple, el navegador envía la solicitud tal cual y comprueba el encabezado Access-Control-Allow-Origin de la respuesta. Si el origen está permitido, entrega la respuesta a JavaScript; si no, bloquea la lectura. Lo que se bloquea es únicamente la lectura desde JavaScript: la solicitud llegó al servidor y su procesamiento se ejecutó. Puede haber datos registrados o actualizados en el servidor aunque en la consola del navegador aparezca un error CORS, y esta diferencia hay que tenerla siempre presente al delimitar un problema durante el desarrollo.

Solicitud preflight (Preflight Request)

Cuando la solicitud no cumple las condiciones de una solicitud simple (por ejemplo PUT, DELETE o una solicitud con encabezados personalizados), el navegador envía antes de la solicitud real una "solicitud preflight" con el método OPTIONS. El servidor responde con los siguientes encabezados para indicar las condiciones que permite.

  • Access-Control-Allow-Origin: el origen permitido
  • Access-Control-Allow-Methods: los métodos HTTP permitidos
  • Access-Control-Allow-Headers: los encabezados de solicitud permitidos
  • Access-Control-Max-Age: el tiempo de caché del resultado del preflight (en segundos)

Si la respuesta del preflight cumple las condiciones de permiso, el navegador envía la solicitud real.

Solicitudes con credenciales

Una solicitud entre orígenes que incluya cookies o un encabezado de autenticación requiere Access-Control-Allow-Credentials: true. En ese caso no se puede usar el comodín (*) en Access-Control-Allow-Origin y hay que indicar un origen concreto. El comodín solo es válido para las solicitudes sin credenciales: se trata de una exclusión establecida en la especificación, así que no existe forma de cumplir ambas cosas a la vez. Lo mismo ocurre con * en Access-Control-Allow-Methods y Access-Control-Allow-Headers, que con credenciales adjuntas no se tratan como comodín.

Otra trampa es que el preflight en sí no lleva cookies (la especificación fija el modo de credenciales del preflight siempre en same-origin). Si se añade una configuración del servidor o del proxy inverso que exige autenticación también para la solicitud OPTIONS, esta falla antes de llegar a la solicitud real y el resultado es un error CORS cuya causa cuesta ver.

Errores de configuración comunes y riesgos de seguridad

Una configuración incorrecta de CORS anula la protección que ofrece la política del mismo origen y genera riesgos de seguridad graves.

  • Uso a la ligera de Access-Control-Allow-Origin: *: permitir todos los orígenes hace que cualquier sitio pueda acceder a la API. No debería usarse fuera de las APIs públicas y nunca en una API que maneje credenciales
  • Reflejar el encabezado Origin sin validarlo: una implementación que copia el valor del encabezado Origin de la solicitud directamente en Access-Control-Allow-Origin equivale en la práctica a un comodín. Debe validarse con una lista blanca
  • Permitir el origen null: permitir Access-Control-Allow-Origin: null habilita el acceso desde iframes en sandbox y desde archivos locales, lo que se puede aprovechar en un ataque
  • Permiso excesivo de subdominios y comparación por sufijo: si se permite mediante una coincidencia de patrón como *.example.com, un atacante que tome el control de un subdominio puede abusar de CORS. Además, una implementación que solo comprueba si el final de la cadena coincide deja pasar incluso otro dominio registrado por el propio atacante, como evil-example.com

Una configuración incorrecta de CORS puede ampliar el daño al combinarse con XSS o CSRF. Por otro lado, endurecer la configuración de CORS no evita CSRF: una solicitud simple como el envío de un formulario llega al servidor sin provocar un preflight, así que CSRF necesita medidas aparte, como tokens o el atributo SameSite de las cookies.

Mejores prácticas para una configuración segura de CORS

A continuación se indican las pautas para configurar CORS de forma segura.

  • Gestionar los orígenes permitidos con una lista blanca: mantener la lista de orígenes permitidos en variables de entorno o en un archivo de configuración y validarla frente al encabezado Origin de la solicitud por coincidencia exacta
  • Permitir solo los métodos y encabezados mínimos necesarios: enumerar en Access-Control-Allow-Methods y Access-Control-Allow-Headers únicamente lo que se usa en la práctica
  • Aprovechar la caché del preflight: indicar Access-Control-Max-Age de forma explícita para reducir la frecuencia de las solicitudes preflight. Cuando no se especifica, el valor predeterminado en la especificación es de solo 5 segundos, por lo que en la práctica sale una solicitud OPTIONS antes de casi cada solicitud. Eso sí, el navegador impone un límite máximo al tiempo de caché y recorta hasta ese límite los valores que lo superan, así que escribir una cifra de segundos extremadamente alta no alarga el efecto
  • Tratar con cuidado las solicitudes con credenciales: al establecer Access-Control-Allow-Credentials: true, restringir estrictamente los orígenes permitidos
  • Usarlo junto con CSP y los encabezados de seguridad: construir una defensa en profundidad combinando CORS con otros encabezados de seguridad en lugar de depender de CORS por sí solo
  • Partir de HTTPS: no incluir orígenes HTTP en la lista de permitidos, para evitar la suplantación del origen mediante un ataque de intermediario

CORS es un control del lado del navegador y no se aplica a la comunicación entre servidores ni a las solicitudes procedentes de herramientas de línea de comandos como curl. No tiene efecto en la vía por la que un atacante llama directamente a la API desde su propio entorno, así que no dependa solo de CORS: implemente siempre autenticación y autorización del lado del servidor.

Cabe añadir que CORS se estandarizó como la recomendación del W3C "Cross-Origin Resource Sharing" (16 de enero de 2014) y después se integró en el estándar Fetch del WHATWG. Cuando se quiere comprobar un comportamiento de detalle, lo más exacto es consultar el estándar Fetch.

Para obtener más información sobre este tema, consulte Encabezados de seguridad HTTP - 5 encabezados esenciales para proteger tu sitio web.

Conceptos erróneos comunes

CORS es una función de seguridad que protege al servidor
CORS es un control del lado del navegador y no protege al servidor de forma directa. Sus restricciones no se aplican a curl ni a la comunicación entre servidores. Lo que en primer lugar prohíbe leer la respuesta de otro origen es la política del mismo origen, y CORS es el mecanismo que relaja esa restricción. Es una capa que protege al usuario del navegador y se sitúa en un nivel distinto de la autenticación y la autorización del lado del servidor.
Si aparece un error CORS, basta con resolverlo con el comodín (*)
El comodín permite el acceso desde todos los orígenes, lo que conlleva un alto riesgo de seguridad. No se puede usar con solicitudes que incluyen credenciales. La solución correcta es agregar solo los orígenes necesarios a la lista blanca.
Compartir

Términos relacionados

Artículos relacionados