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 respuesta | Significado |
|---|---|
Access-Control-Allow-Origin | Qué orígenes pueden leer la respuesta |
Access-Control-Allow-Credentials | Si se permiten solicitudes con credenciales (cookies) |
Access-Control-Allow-Methods / -Headers | Qué 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érmino | Qué decide | Quién lo aplica |
|---|---|---|
| Política del mismo origen | Otros orígenes no pueden leer las respuestas (comportamiento por defecto del navegador) | El navegador |
| CORS | Como excepción a lo anterior, qué orígenes pueden leer | El servidor lo declara, el navegador lo aplica |
| CSRF | Un ataque que hace que el navegador del usuario, con sesión iniciada, envíe solicitudes no deseadas | Se evita con tokens CSRF y SameSite |
| CSP | Qué scripts y conexiones puede cargar tu propia página | El 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
*
Cómo suelen producirse las malas configuraciones
La mayoría nacen de intentar que desaparezca un error de CORS. La secuencia típica:
- Llamar a la API desde el front end con cookies produce un error de CORS
- 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 - Pasas a devolver el
Originque 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 comoevil-example.com - Comprobar que «empieza por
https://example.com» deja pasar tambiénhttps://example.com.evil.net - Permitir el origen
nullpor comodidad en desarrollo permite también lecturas desde orígenesnullque otros sitios pueden generar
Compara el origen completo (esquema, host y puerto) buscando una coincidencia exacta.
Los fundamentos de un ajuste seguro
Usa una lista de permitidos (lo más importante)
No reflejes el Origin a ciegas
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.Nunca uses * con credenciales
Access-Control-Allow-Origin: * con Allow-Credentials: true. Nombra en su lugar un origen concreto.Minimiza lo que abres
* 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: *yAccess-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
- Glosario: Qué es XSS · Qué es CSRF · Qué es un JWT
- Aprende: Elegir los encabezados de seguridad (en inglés)
- Comprueba: Verificador de encabezados de seguridad · Auditoría de seguridad del sitio