Seguridad de APIs · CORS · Desarrollo Web · Headers HTTP · Seguridad del Navegador · Política del Mismo Origen
CORS Explicado para Desarrolladores: Preflight, Wildcards y Configuración Segura
Una guía práctica del Cross-Origin Resource Sharing para desarrolladores independientes y equipos pequeños. Aprende cómo funcionan las solicitudes preflight, por qué los wildcards rompen con credenciales, y cómo configurar CORS sin exponer tu API al abuso.
Publicado:
La Respuesta Corta
CORS (Cross-Origin Resource Sharing) es un mecanismo de seguridad impuesto por el navegador que permite a los servidores indicar a los navegadores qué orígenes tienen permiso para leer sus respuestas. Cuando tu JavaScript frontend hace una solicitud a un origen diferente, el navegador verifica los headers de CORS antes de devolver la respuesta a tu código. Configurar mal estos headers, especialmente usar wildcards con credenciales, es uno de los errores de seguridad más comunes y peligrosos que cometen los desarrolladores independientes.
Qué es CORS Realmente
La política del mismo origen es una regla fundamental de seguridad del navegador: los scripts cargados desde un origen no pueden leer respuestas de otro origen a menos que se permita explícitamente. Un origen se define por tres componentes: esquema, host y puerto. https://api.ejemplo.com:443 y https://api.ejemplo.com:8443 son orígenes diferentes. https://api.ejemplo.com y http://api.ejemplo.com son orígenes diferentes. Incluso https://api.ejemplo.com y https://www.api.ejemplo.com son orígenes diferentes porque el host difiere.
CORS es el mecanismo que relaja esta restricción de forma controlada. Funciona a través de headers HTTP. Cuando tu servidor responde a una solicitud cross-origin, incluye headers como Access-Control-Allow-Origin para decirle al navegador si la respuesta puede ser leída por el script solicitante. Sin estos headers, el navegador bloquea la respuesta y tu JavaScript recibe un error.
Esto no es un mecanismo de autenticación ni de autorización. CORS controla si el navegador expone la respuesta a tu código; no previene solicitudes HTTP directas desde herramientas como curl o Postman. Si tu API devuelve datos sensibles sin autenticación adecuada, una mala configuración de CORS no te salvará.
Cómo Funcionan las Solicitudes Preflight
No todas las solicitudes cross-origin generan tráfico adicional. Las solicitudes simples—típicamente GET o POST con tipos de contenido estándar como application/x-www-form-urlencoded, multipart/form-data, o text/plain—proceden directamente. El navegador envía la solicitud real y verifica los headers de respuesta.
Pero las solicitudes que usan métodos como PUT, DELETE, o PATCH, o que envían headers personalizados, o que usan tipos de contenido no estándar, activan un preflight. El navegador envía primero una solicitud OPTIONS al origen destino, preguntando al servidor qué métodos y headers están permitidos. Solo después de que el servidor responda con aprobación, el navegador envía la solicitud real.
Verás esto en la pestaña de red de tu navegador como dos solicitudes: primero una solicitud OPTIONS, luego la GET, POST o cualquier método que pretendías. La respuesta preflight incluye headers como Access-Control-Allow-Methods y Access-Control-Allow-Headers para decirle al navegador qué está permitido hacer.
Este proceso de dos pasos existe para prevenir que scripts maliciosos obliguen a tu navegador a hacer solicitudes destructivas o sensibles a APIs con las que interactúas diariamente. Si una página maliciosa pudiera desencadenar una solicitud DELETE a tu API bancaria sin que el servidor lo permita explícitamente, el daño sería severo. El preflight es la forma en que el navegador pide permiso primero.
La Trampa del Wildcard: Por Qué * Falla con Credenciales
Aquí es donde la mayoría de los desarrolladores cometen errores críticos. El header Access-Control-Allow-Origin acepta要么 un origen específico como https://app.ejemplo.com o el wildcard * para permitir cualquier origen.
Aquí está la regla que debes recordar: no puedes usar Access-Control-Allow-Origin: * cuando tu API requiere credenciales. Las credenciales incluyen cookies, autenticación HTTP y certificados TLS del lado del cliente. Si tu frontend envía credenciales con solicitudes a tu API—y muchos lo hacen, especialmente para sesiones autenticadas—establecer el header allow-origin a * causará que el navegador rechace la respuesta completamente, aunque la solicitud tenga éxito del lado del servidor.
Algunos desarrolladores intentan solucionar esto reflejando el header Origin de vuelta como valor de Access-Control-Allow-Origin. Esto parece elegante: el servidor acepta solicitudes de cualquier origen y lo refleja de vuelta. Pero este enfoque es peligroso cuando hay credenciales involucradas. Un atacante puede alojar una página maliciosa en malicioso.com que haga solicitudes con credenciales a tu API. Tu servidor refleja malicioso.com de vuelta como el origen permitido, y el navegador otorga a la página maliciosa acceso a las respuestas de tu API, incluyendo cookies o tokens de autenticación. Esta es una vulnerabilidad clásica de mala configuración CORS, clasificada bajo la categoría de Configuración Insegura de OWASP.
El enfoque correcto es binario y deliberado:
- Si tu API es pública y no maneja credenciales, establece
Access-Control-Allow-Origin: *y omiteAccess-Control-Allow-Credentialscompletamente. - Si tu API requiere credenciales, establece
Access-Control-Allow-Origina los origin(es) exacto(s) que necesitan acceso, e incluyeAccess-Control-Allow-Credentials: true. - Nunca reflejes el header
Origincuando las credenciales estén en juego.
Configurar CORS de Forma Segura: Una Lista de Verificación Práctica
1. Escopa tus headers CORS solo a endpoints de API. Si tu servidor sirve tanto un sitio web como una API, no agregues headers CORS a cada respuesta. Solo los endpoints de API que necesitan acceso cross-origin deben devolver estos headers. Agregarlos a activos estáticos o páginas internas crea superficie de ataque innecesaria.
2. Usa orígenes explícitos, no wildcards, para APIs autenticadas. Mantén una lista de orígenes permitidos en tu configuración. Si tienes múltiples aplicaciones frontend—tal vez un dashboard en https://dashboard.ejemplo.com y un backend de app móvil en https://app.ejemplo.com—lista cada uno explícitamente. Esto es más trabajo que *, pero es el único enfoque seguro para acceso con credenciales.
3. Maneja las solicitudes preflight correctamente. Tu servidor debe responder a solicitudes OPTIONS con los headers apropiados. Si estás usando un framework, verifica si maneja el preflight automáticamente o si necesitas agregar middleware. Las respuestas preflight faltantes causarán que tu frontend falle con errores confusos que parecen problemas de red pero son rechazos CORS en realidad.
4. Establece Access-Control-Allow-Headers con precisión. Si tu frontend envía headers personalizados como X-Request-ID o Authorization, la respuesta preflight debe incluir esos headers en Access-Control-Allow-Headers. Si usas * aquí, algunos navegadores lo rechazarán cuando las credenciales estén involucradas. Lista los headers específicos que tu API espera.
5. Usa Access-Control-Max-Age para reducir la sobrecarga de preflight. Las solicitudes preflight agregan latencia. Si tu API consistentemente acepta los mismos métodos y headers, puedes decirle a los navegadores que cacheen la respuesta preflight por un período de tiempo usando Access-Control-Max-Age. Esto reduce la cantidad de solicitudes OPTIONS sin sacrificar seguridad.
6. Nunca confíes en CORS para control de acceso. CORS es un mecanismo del navegador, no un control de seguridad del servidor. Un atacante determinado puede omitir CORS completamente haciendo solicitudes desde código del lado del servidor, herramientas de línea de comandos o clientes personalizados. Siempre implementa autenticación y autorización adecuadas en tus endpoints de API independientemente de la configuración CORS.
Patrones Comunes de Mala Configuración CORS y sus Riesgos
Reflejar el header Origin. Como se discutió, esto permite que cualquier origen haga solicitudes con credenciales. El riesgo es robo de credenciales y acceso no autorizado a datos desde páginas maliciosas de terceros.
Access-Control-Allow-Methods demasiado amplio. Permitir todos los métodos HTTP cuando tu API solo necesita GET y POST expande la superficie de ataque. Un atacante podría usar operaciones DELETE o PATCH si tu configuración CORS los permite.
Manejo preflight ausente. Algunos frameworks devuelven 405 Method Not Allowed para solicitudes OPTIONS. Esto rompe silenciosamente las solicitudes cross-origin que requieren preflight, causando frustración en desarrolladores frontend que pueden recurrir a soluciones inseguras como extensiones CORS del navegador o proxies del lado del servidor.
Headers CORS en endpoints no-API. Devolver headers CORS en cada respuesta, incluyendo páginas estáticas y rutas internas, viola el principio de menor privilegio. Da a cada origen la capacidad de leer respuestas de endpoints que nunca estuvieron destinados para acceso cross-origin.
FAQ
¿Protege CORS mi API del acceso no autorizado? No. CORS solo controla lo que el navegador expone a JavaScript. No autenticar usuarios ni autorizar solicitudes. Cualquiera puede hacer solicitudes HTTP directas a tu API sin pasar por un navegador. Siempre implementa autenticación y autorización por separado.
¿Por qué recibo errores CORS aunque mi API funciona en Postman? Postman no aplica la política del mismo origen. CORS es una característica de seguridad del navegador, no una restricción de API. Tu API puede estar perfectamente funcional; el navegador simplemente está bloqueando la respuesta porque los headers CORS faltan o son incorrectos.
¿Puedo usar un wildcard para Access-Control-Allow-Headers?
En algunos casos, sí. Pero cuando Access-Control-Allow-Credentials está establecido en true, ciertos navegadores aplican reglas más estrictas. Es más seguro listar los headers específicos que tu API requiere en lugar de depender de un comportamiento wildcard que puede variar entre implementaciones de navegadores.
¿Cuál es la diferencia entre CORS y CSRF? CORS y CSRF a menudo se confunden pero abordan problemas diferentes. CORS controla el acceso de lectura cross-origin desde scripts del navegador. CSRF explota sesiones autenticadas engañando al navegador del usuario para que haga solicitudes no deseadas. Una configuración CORS adecuada no previene CSRF; necesitas protecciones CSRF separadas como tokens anti-falsificación.
¿Debería deshabilitar CORS completamente para desarrollo? Deshabilitar CORS con extensiones o marcas del navegador es una conveniencia de desarrollo, no una solución. Enmascara problemas de configuración que aparecerán en producción. Corrige los headers correctamente en lugar de buscar soluciones alternativas.
Conclusión
CORS es un mecanismo necesario para el desarrollo web moderno, pero es fácil de configurar mal. El principio central es simplicidad y deliberación: permite solo los orígenes que necesitan acceso, sé explícito sobre las credenciales, maneja las solicitudes preflight correctamente, y nunca trates CORS como un control de seguridad. La seguridad real de tu API proviene de la autenticación, autorización, validación de entradas y limitación de velocidad—no de headers HTTP que los navegadores eligen aplicar.
