Tema 14
Cuando una API es consumida desde navegadores, entra en juego un modelo de seguridad adicional: el del propio browser. CORS y otras cabeceras relacionadas no reemplazan autenticación ni autorización, pero condicionan qué orígenes web pueden interactuar con la API y cómo se expone el servicio a clientes ejecutándose en el entorno más adversarial de todos: el navegador del usuario.
Muchas APIs modernas son consumidas por SPAs, dashboards, portales administrativos o integraciones web que se ejecutan en el navegador. En ese contexto, no basta con pensar en el backend; también hay que entender qué controles aplica el browser y cómo una configuración inapropiada puede abrir acceso a orígenes no previstos.
CORS suele ser malinterpretado. A veces se configura de forma demasiado abierta para “hacer que funcione”, y otras veces se le atribuyen propiedades que no tiene. El resultado son APIs que exponen más de lo necesario o equipos que creen estar protegidos por una política que en realidad no reemplaza controles de acceso reales.
Este tema explica qué resuelve CORS, qué no resuelve y cómo combinarlo con cabeceras y diseño de exposición segura para clientes web.
Los navegadores aplican una política llamada Same-Origin Policy para restringir cómo un documento cargado desde un origen puede interactuar con recursos de otro origen. Un origen se define, en términos simples, por combinación de esquema, host y puerto.
Esta política busca reducir el impacto de páginas maliciosas que intentan leer respuestas de otros sitios a los que el navegador del usuario también tiene acceso. CORS aparece como un mecanismo controlado para relajar esa restricción cuando un servidor desea permitir acceso cruzado legítimo.
Cross-Origin Resource Sharing es un conjunto de cabeceras y reglas mediante el cual un servidor declara qué orígenes web pueden leer respuestas o realizar ciertos tipos de solicitudes desde el navegador. En otras palabras, le indica al browser bajo qué condiciones puede compartir recursos con scripts cargados desde otros orígenes.
CORS no es un mecanismo de autenticación ni de autorización del lado servidor. Es una política que el navegador hace cumplir cuando un script web intenta consumir una API cross-origin.
| CORS ayuda a controlar | CORS no resuelve por sí solo |
|---|---|
| Qué orígenes web pueden leer respuestas desde el browser | Autorización real sobre recursos |
| Qué métodos y cabeceras están permitidos en cross-origin | Protección contra clientes no browser |
| Uso de credenciales en solicitudes web cross-origin | CSRF, si no se diseña el flujo adecuadamente |
| Exposición de respuestas a scripts de otros orígenes | Exposición por tokens robados o APIs públicas mal diseñadas |
Las políticas CORS suelen expresarse con cabeceras específicas. Comprenderlas es clave para no abrir más de lo debido.
Permitir cualquier origen puede parecer conveniente, pero amplía la superficie de exposición. En APIs realmente públicas y sin credenciales puede ser aceptable en algunos casos, pero en APIs con datos sensibles, sesiones o tokens enviados automáticamente, suele ser una mala decisión.
El riesgo aumenta cuando se combina con:
La práctica más segura suele ser mantener una allowlist explícita de orígenes confiables. Eso obliga a decidir conscientemente qué frontends, dominios o subdominios deben poder consumir la API desde un navegador.
Esto implica cuidar:
Cuando una API permite `Access-Control-Allow-Credentials`, autoriza al navegador a enviar cookies, autenticación HTTP u otras credenciales asociadas al contexto del usuario. Esto vuelve mucho más delicada la política CORS, porque ya no se trata solo de exponer una API pública sino de permitir acceso cross-origin con identidad implícita.
En ese escenario, no es correcto usar `*` como origen permitido. La política debe ser estricta y específica.
Para ciertas solicitudes cross-origin, el navegador realiza primero una petición `OPTIONS` llamada preflight. Su objetivo es preguntar al servidor si la operación real estará permitida bajo CORS.
Esto ocurre típicamente cuando:
Configurar mal las respuestas preflight puede dejar métodos o cabeceras habilitados sin intención real.
Una API no debería anunciar más métodos o cabeceras de los que realmente necesita para el cliente web autorizado. Cuanto más amplio el permiso, mayor el margen para uso inesperado o abuso desde un origen comprometido.
Buenas prácticas:
Por defecto, el navegador no expone todas las cabeceras de respuesta al JavaScript del cliente. Si una API declara `Access-Control-Expose-Headers`, está ampliando qué metadata puede leer el script.
Esto es útil en algunos casos, pero también puede revelar:
Conviene exponer solo cabeceras que el cliente realmente necesita para funcionar.
Algunas implementaciones responden reflejando el origen recibido si parece válido. Esto puede ser correcto si se valida contra una allowlist estricta. Pero si la validación es débil o ingenua, puede terminar permitiendo orígenes no previstos.
Errores típicos:
No todas las APIs necesitan CORS estricto. Si una API es verdaderamente pública, sin credenciales ni datos sensibles, permitir acceso amplio desde navegadores puede ser una decisión válida. Pero eso debe responder a una intención explícita de producto, no a una configuración por defecto para “evitar problemas”.
La pregunta correcta es: ¿realmente queremos que cualquier sitio pueda usar esta API desde el browser del usuario?
Una confusión habitual es creer que una política CORS restrictiva elimina otros riesgos. No es así. CORS regula qué puede leer el JavaScript del navegador desde otro origen, pero no define por sí solo autorización ni elimina la necesidad de proteger flujos con cookies frente a CSRF cuando corresponda.
Tampoco evita que un cliente no browser llame directamente a la API si tiene red y credenciales suficientes.
Además de CORS, existen otras cabeceras que ayudan a reforzar la interacción segura con clientes web. No todas aplican igual a una API JSON, pero algunas siguen siendo relevantes según el contexto de consumo.
| Cabecera | Propósito |
|---|---|
| Content-Type | Declarar claramente el tipo de respuesta |
| Cache-Control | Controlar almacenamiento de respuestas sensibles |
| Strict-Transport-Security | Reforzar uso exclusivo de HTTPS |
| X-Content-Type-Options | Evitar interpretaciones de tipo no deseadas en ciertos clientes |
| Referrer-Policy | Limitar exposición de URLs y contexto de navegación |
Una API no debería exponerse igual a todos los tipos de clientes. Cuando hay frontends web concretos, conviene diseñar la exposición específicamente para ellos: orígenes definidos, métodos precisos, cabeceras mínimas, credenciales controladas y políticas distintas según ambiente o aplicación.
También puede ser razonable separar APIs públicas, privadas y administrativas en dominios o rutas distintas para evitar políticas CORS excesivamente complejas o ambiguas.
Las políticas CORS tienden a degradarse con el tiempo si no se gobiernan. Se agregan orígenes temporales, ambientes de staging, dominios viejos o comodines “provisorios” que luego quedan en producción.
Por eso es importante:
CORS y las cabeceras relacionadas son parte esencial de la seguridad de una API cuando intervienen navegadores. No protegen la API por sí solos, pero sí definen qué frontends pueden interactuar con ella de manera controlada y cuánto del servicio queda expuesto al entorno web. Configurarlos bien exige precisión, intención y mantenimiento continuo; configurarlos mal suele abrir más superficie de la que el equipo imagina.
En el próximo tema estudiaremos rate limiting, cuotas, throttling y defensa frente a abuso y denegación de servicio, para analizar cómo controlar el consumo de la API y resistir automatización agresiva.