Glosario

Qué es CORS: cómo funciona y qué expone una mala configuración

CORS controla si el JavaScript de otro origen puede leer las respuestas de tu API. Por qué una configuración permisiva expone datos y cómo configurarlo bien.

Publicado 2026-06-11 Actualizado 2026-10-06 7 min de lectura
En esta página

Que una API funcione tras añadir Access-Control-Allow-Origin no significa que sea segura. CORS no es algo que se añade una vez y se olvida; lo importante es acotar a quién permites. Aquí tienes cómo funciona y cómo configurarlo de forma segura (sin pasos de ataque).

Primero, cómo funciona

Los navegadores aplican una política del mismo origen: el JS de otro origen no puede leer libremente las respuestas de otro sitio. CORS existe para abrir esa excepción solo a las partes que el servidor permite explícitamente.

Un origen es la combinación de esquema (https), nombre de host y puerto. https://example.com y https://api.example.com tienen hosts distintos, así que son orígenes distintos; http://example.com y https://example.com difieren en el esquema, así que también lo son.

Encabezado de respuestaSignificado
Access-Control-Allow-OriginQué orígenes pueden leer la respuesta
Access-Control-Allow-CredentialsSi se permiten solicitudes con credenciales (cookies)
Access-Control-Allow-Methods / -HeadersQué métodos/encabezados HTTP se permiten

El punto clave: CORS está del lado de "relajar la restricción". Relájala mal y datos que no deberían poder leerse se vuelven legibles para terceros.

Otra cosa importante: lo que CORS impide es que JavaScript lea la respuesta. Una solicitud simple llega al servidor y se procesa incluso sin permiso de CORS. En casos como los encabezados personalizados, el navegador comprueba primero con una solicitud OPTIONS (un preflight) y no envía la solicitud real si no está permitida.

En qué se diferencia de mecanismos parecidos

CORS se confunde a menudo con mecanismos de nombre parecido y con una vulnerabilidad muy relacionada.

TérminoQué decideQuién lo aplica
Política del mismo origenOtros orígenes no pueden leer las respuestas (comportamiento por defecto del navegador)El navegador
CORSComo excepción a lo anterior, qué orígenes pueden leerEl servidor lo declara, el navegador lo aplica
CSRFUn ataque que hace que el navegador del usuario, con sesión iniciada, envíe solicitudes no deseadasSe evita con tokens CSRF y SameSite
CSPQué scripts y conexiones puede cargar tu propia páginaEl servidor lo declara, el navegador lo aplica

CORS trata de "si la respuesta puede leerse"; CSRF trata de "si se puede enviar una solicitud en nombre del usuario". Endurecer CORS no evita el CSRF.

Qué hace peligroso un ajuste

Ajustes peligrosos (comunes)

  • Reflejar el Origin de la solicitud como permitido
  • Access-Control-Allow-Origin: * junto con permitir credenciales
  • Abrir APIs ampliamente de origen cruzado que no lo necesitan

Ajustes seguros

  • Una lista de permitidos (solo orígenes fijos y de confianza)
  • Denegar por defecto todo lo demás
  • Para solicitudes con credenciales, un origen concreto, no *
El JS del sitio del atacante solicita tu API (el usuario tiene sesión iniciada)
↓ el servidor permite el Origin a ciegas (mala configuración)
el navegador permite leer la respuesta = datos personales/tokens llegan al sitio del atacante
Con un ajuste laxo, el JS del sitio del atacante puede usar el estado con sesión iniciada del usuario para leer la respuesta de la API.

Cómo suelen producirse las malas configuraciones

La mayoría nacen de intentar que desaparezca un error de CORS. La secuencia típica:

  1. Llamar a la API desde el front end con cookies produce un error de CORS
  2. Añades Access-Control-Allow-Origin: *. Pero en solicitudes con credenciales el navegador no deja que la página lea una respuesta con *, así que el error sigue
  3. Pasas a devolver el Origin que llega, y el error desaparece. Al mismo tiempo, cualquier sitio puede leer ya las respuestas con sesión iniciada

Incluso con una lista de permitidos, una comparación descuidada lleva al mismo sitio.

  • Comprobar que «termina en example.com» deja pasar también otro dominio como evil-example.com
  • Comprobar que «empieza por https://example.com» deja pasar también https://example.com.evil.net
  • Permitir el origen null por comodidad en desarrollo permite también lecturas desde orígenes null que otros sitios pueden generar

Compara el origen completo (esquema, host y puerto) buscando una coincidencia exacta.

Los fundamentos de un ajuste seguro

1

Usa una lista de permitidos (lo más importante)

Permite solo orígenes de confianza decididos de antemano y deniega el resto (denegar por defecto). No "permitas todo sin más".
2

No reflejes el Origin a ciegas

No copies el Origin de la solicitud directamente en Access-Control-Allow-Origin. Devuélvelo solo cuando coincida con la lista de permitidos. Como el valor cambia según la solicitud, envía también Vary: Origin para que las cachés no mezclen respuestas.
3

Nunca uses * con credenciales

No combines Access-Control-Allow-Origin: * con Allow-Credentials: true. Nombra en su lugar un origen concreto.
4

Minimiza lo que abres

Solo los endpoints que de verdad necesiten ser de origen cruzado. No abras ampliamente. Usa * solo para datos públicos que no requieren inicio de sesión.

Cómo revisar tu propio sitio

Envía a tu API una solicitud que declare otro origen y mira los encabezados de la respuesta. Hazlo solo con API que gestiones tú.

# Declara un origen que no has permitido y mira solo los encabezados de respuesta
curl -s -o /dev/null -D - -H "Origin: https://example.com" https://your-api.example/ | grep -i "^access-control"
  • Si recibes Access-Control-Allow-Origin: https://example.com, la API refleja el Origin. Añade la comparación con la lista de permitidos
  • Si aparecen a la vez Access-Control-Allow-Origin: * y Access-Control-Allow-Credentials: true, revisa el ajuste
  • Si no vuelve nada, o solo el origen de tu propio front end, está bien

Repite la misma comprobación con Origin: null. Para un sitio cuya propiedad hayas verificado, la auditoría de seguridad del sitio revisa a la vez el reflejo del origen y los ajustes de credenciales. Los demás encabezados de seguridad puedes revisarlos con el verificador de encabezados de seguridad.

La opinión de este sitio: CORS es 'cómo relajas', no 'la defensa' — acota a quién abres

Un error común es pensar que "añadir encabezados CORS lo hace seguro". Es lo contrario: CORS relaja la restricción del navegador. Así que lo importante no es "cómo lo añades", sino "minimizar a quién, y hasta dónde, abres".

En este sitio separamos los datos que es correcto leer desde fuera de los que no, y recomendamos denegar por defecto, con una lista de permitidos que abra solo las excepciones. Ten en cuenta que XSS (que rompe la premisa de lectura) y CSRF (que apunta a cambios de estado) son fallos distintos: CORS por sí solo no lo cubre todo.

Fuentes (primarias)

  • MDN Web Docs, «Cross-Origin Resource Sharing (CORS)» (en inglés) — developer.mozilla.org (no usar * en solicitudes con credenciales, condiciones del preflight, Vary: Origin)
  • WHATWG Fetch Standard, «CORS protocol» (en inglés) — fetch.spec.whatwg.org
  • OWASP HTML5 Security Cheat Sheet (en inglés) — cheatsheetseries.owasp.org (permitir solo dominios de confianza; no devolver el encabezado Origin sin comprobarlo)
  • PortSwigger Web Security Academy, «Cross-origin resource sharing (CORS)» (en inglés) — portswigger.net (errores al comparar con la lista de permitidos, el origen null, CORS no protege contra CSRF)
  • MITRE CWE-942: Permissive Cross-domain Policy with Untrusted Domains (en inglés) — cwe.mitre.org

Sigue leyendo

Clasificación

CWE clasifica los tipos de debilidad que causan las vulnerabilidades; MITRE ATT&CK clasifica las técnicas de los atacantes. Ambos los mantiene MITRE y se usan ampliamente para etiquetar vulnerabilidades y reglas de detección.

CWE™: Copyright © 2006–2026, The MITRE Corporation. CWE is a trademark of The MITRE Corporation. The MITRE Corporation hereby grants you a non-exclusive, royalty-free license to use CWE for research, development, and commercial purposes. Any copy you make for such purposes is authorized on the condition that you reproduce MITRE’s copyright designation and this license in any such copy. CWE Terms of Use

FAQ

Q¿Para qué sirve CORS?
A

Los navegadores aplican una política del mismo origen: el JavaScript de un origen (sitio) no puede leer libremente las respuestas de otro sitio. CORS (Cross-Origin Resource Sharing) es cómo un servidor abre esa excepción, pero solo a las partes que permite explícitamente. Encabezados de respuesta como Access-Control-Allow-Origin declaran qué orígenes pueden leer la respuesta.

Q¿Qué expone una mala configuración de CORS?
A

Unos permisos demasiado laxos pueden dejar que el sitio de un atacante lea, vía JavaScript, las respuestas de la API de un usuario con sesión iniciada. Casos clásicos: reflejar como permitido el Origin de la solicitud, o poner Access-Control-Allow-Origin a * permitiendo además credenciales (cookies). El resultado es que datos personales o tokens pueden llegar a un tercero.

Q¿Cuál es la base de un ajuste seguro?
A

Una lista de permitidos. Permite solo orígenes de confianza decididos de antemano y deniega el resto (denegar por defecto). No reflejes a ciegas el Origin de la solicitud. Para solicitudes con credenciales, no uses * en Access-Control-Allow-Origin: nombra un origen concreto. Y expón solo los endpoints que de verdad necesiten ser de origen cruzado.

QMe sale un error de CORS: ¿pongo Access-Control-Allow-Origin a *?
A

Para una API que solo devuelve datos públicos sin inicio de sesión, * está bien. Pero en solicitudes que llevan credenciales como cookies, el navegador no deja que la página lea una respuesta con *, así que el error sigue. Si entonces pasas a devolver cualquier Origin que llegue, cualquier sitio podrá leer la respuesta. La solución correcta es poner el origen de tu propio front end en una lista de permitidos y devolverlo solo cuando coincida exactamente.

Q¿Configurar CORS evita el CSRF?
A

No. CORS controla si una respuesta puede leerse, no si una solicitud se envía. Las solicitudes simples (como un POST con forma de envío de formulario) llegan al servidor incluso sin permiso de CORS. El CSRF se evita con tokens CSRF y el atributo SameSite de las cookies.

Q¿Qué es una solicitud preflight?
A

Antes de enviar la solicitud real, el navegador pregunta al servidor con una solicitud OPTIONS si ese método y esos encabezados están permitidos. Ocurre con encabezados personalizados, un POST con application/json, PUT, DELETE y similares. Si no está permitido, la solicitud real no se envía. Las solicitudes simples —GET, HEAD o POST usando solo los encabezados y tipos de contenido permitidos— no la provocan.