De todas las medidas de seguridad que existen, estas son probablemente las que mejor relación tienen entre esfuerzo y protección: se configuran una vez en el servidor, no cambian el código de la aplicación y cierran categorías enteras de ataque.
También son de las peor entendidas. Sus nombres son siglas parecidas, actúan en momentos distintos del recorrido de una petición y una de ellas, CORS, se malinterpreta de forma tan sistemática que la mayoría de la gente cree que hace justo lo contrario de lo que hace.
Qué es una cabecera y por qué importa
Cada vez que un servidor responde algo, además del contenido envía una serie de líneas con instrucciones para el navegador. Son las cabeceras, y el navegador las obedece.
Ahí está la clave de todo este tema: estas protecciones no las aplica tu servidor, las aplica el navegador de quien visita porque tu servidor se lo pidió. Son instrucciones a un programa que no controlas y que, afortunadamente, hace caso.
Esa naturaleza explica sus límites, que es lo que más confusión genera. Una cabecera puede impedir que el navegador de una persona haga algo peligroso. No puede impedir que un programa sin navegador haga lo que quiera, porque ese programa simplemente ignora las instrucciones.
Tenerlo presente evita el error más común de esta materia, que es confiar en una cabecera para proteger algo que hay que proteger en el servidor.
Cada una actúa en un momento distinto
La forma más clara de ordenarlas no es por importancia sino por el instante en que intervienen.
Esa separación resuelve la confusión habitual. No compiten ni se sustituyen: un sitio bien configurado tiene las tres, porque cada una cubre un hueco que las otras dejan abierto.
HSTS: que nunca viaje sin cifrar
Es la más sencilla de las tres y la que menos discusión genera. Le dice al navegador que, a partir de ahora, con este dominio hable siempre cifrado, sin excepciones.
El problema que resuelve es sutil. Aunque el sitio redirija automáticamente a la versión segura, esa primera petición insegura existe y puede ser interceptada por quien esté en la misma red. Con esta cabecera, el navegador ni siquiera llega a hacerla: recuerda la instrucción y va directo a la versión cifrada.
La precaución que hay que tomar es que la instrucción tiene memoria. Si se configura con una duración larga y después el sitio deja de funcionar correctamente con cifrado, los visitantes que ya la recibieron no podrán entrar hasta que caduque.
Por eso la práctica sensata es empezar con una duración corta, comprobar que todo el sitio y sus subdominios funcionan bien, y solo entonces subirla al valor largo habitual.
CORS: lo que casi todo el mundo entiende al revés
Aquí está el malentendido más extendido de la seguridad web, y conviene enunciarlo sin rodeos: CORS no protege tu API.
Los navegadores tienen una regla antigua que impide que el código de un sitio lea respuestas de otro dominio. Esa regla protege a los usuarios: evita que una página cualquiera, abierta en otra pestaña, lea los datos de tu correo aprovechando que tienes sesión iniciada.
CORS es el mecanismo para relajar esa regla de forma controlada. Sirve para decir qué otros dominios sí pueden leer tus respuestas. Es decir, no añade una restricción: abre una puerta que estaba cerrada.
De ahí la consecuencia que más sorprende. Configurar CORS de forma estricta no impide que nadie llame a tu API: cualquiera puede hacerlo desde un programa, desde la terminal o desde un servidor, porque esos clientes no tienen la regla del navegador. Lo único que CORS controla es qué páginas web pueden leer la respuesta.
Cuando algo falla por CORS, la solución que aparece en todos los foros es permitir cualquier origen. Eso hace desaparecer el error.
Y si además la API acepta credenciales, acaba de autorizarse a que cualquier sitio de internet haga peticiones autenticadas en nombre de tus usuarios. El error desapareció; el problema es mucho mayor que el error.
La regla práctica: si una ruta necesita protección, la protección es autenticación y permisos en el servidor. CORS se configura con la lista concreta de dominios propios que deben poder llamar, y nunca con un comodín cuando hay credenciales de por medio.
CSP: limitar qué código puede ejecutarse
Es la más potente de las tres y también la más trabajosa. Define de qué sitios puede cargar recursos la página y, sobre todo, qué scripts pueden ejecutarse.
El ataque que previene es la inyección de código: cuando alguien consigue meter un script en tu página, por ejemplo a través de un comentario mal filtrado, y ese script se ejecuta con todos los privilegios de la sesión del visitante.
Con una política estricta, el navegador se niega a ejecutar código que no provenga de las fuentes autorizadas, así que la inyección deja de tener efecto aunque haya ocurrido. Es una segunda línea de defensa que actúa cuando la primera, filtrar bien lo que entra, ha fallado.
La dificultad es que casi ningún sitio existente cumple una política estricta a la primera: hay scripts incrustados, herramientas de terceros y estilos en línea que la incumplen. Por eso la forma realista de adoptarla es en modo de solo informar, que no bloquea nada y reporta lo que habría bloqueado, para ir corrigiendo antes de aplicarla de verdad.
Las que se ponen en un minuto
Además de las tres principales hay un grupo de cabeceras menores que no requieren ninguna decisión y conviene tener puestas.
- Impedir que el sitio se muestre dentro de otro. Evita el engaño de superponer una página invisible sobre otra para que el usuario pulse donde no cree estar pulsando.
- Impedir que el navegador adivine el tipo de archivo. Sin esto, un archivo subido por un usuario puede acabar interpretándose como código.
- Controlar qué información se envía al salir del sitio. Por defecto, al seguir un enlace se transmite la dirección de origen completa, que puede contener datos que no deberían salir.
- Restringir el acceso a cámara, micrófono y ubicación. Si la aplicación no los usa, conviene declararlo para que ningún componente de terceros los solicite.
Ninguna de estas exige pensar en el caso concreto: se ponen igual en casi cualquier sitio y cierran huecos que de otro modo quedan abiertos por omisión.
Cómo comprobar lo que tienes hoy
Antes de configurar nada conviene ver el estado actual, que casi nunca es el que uno cree, sobre todo si hay una red de distribución o un proxy en medio que puede estar añadiendo o quitando cabeceras.
La comprobación es directa y no requiere ninguna herramienta especial:
# Muestra solo las cabeceras que devuelve tu sitio
curl -sI https://midominio.com
# Filtrando las de seguridad
curl -sI https://midominio.com | grep -i "strict-transport\|content-security\|x-frame\|x-content-type"
Conviene repetirlo después de cualquier cambio de infraestructura. Es frecuente que una configuración correcta en el servidor de aplicación desaparezca al pasar por una capa intermedia, y nadie se entera porque nada falla.
Dónde se configuran, y por qué a veces desaparecen
Las cabeceras se pueden emitir desde varios sitios, y esa es la causa de que a menudo el resultado no sea el esperado: la aplicación las pone, y algo en el camino las sustituye.
Pueden salir del código de la aplicación, de la configuración del servidor web que tiene delante, de la plataforma donde está alojada o de la red de distribución que sirve el contenido. Cuando hay varias capas, la última que toca la respuesta es la que gana.
El caso típico es un equipo que añade las cabeceras en la aplicación, comprueba que están y meses después alguien pone una CDN delante que no las propaga. Nada falla, nadie recibe ningún aviso, y la protección dejó de existir.
Por eso conviene decidir a conciencia en qué capa viven y dejarlo anotado. Ponerlas en el punto más externo, el que ve todas las respuestas, suele ser lo más fiable, porque cubre también los archivos estáticos y las páginas de error, que son las que más veces se quedan sin protección.
Y la comprobación debe hacerse siempre desde fuera, contra la dirección pública real, no contra el servidor de aplicación. Es la única forma de ver lo que recibe de verdad un visitante.
Una nota sobre las cabeceras que ya no sirven
Este terreno acumula recomendaciones antiguas que siguen circulando, y aplicarlas hoy va desde lo inútil hasta lo contraproducente.
El ejemplo más claro es la vieja cabecera de protección contra scripts inyectados, que los navegadores modernos ignoran por completo. En su día llegó a introducir problemas propios, y la recomendación actual es desactivarla explícitamente y confiar en CSP, que es lo que la sustituye.
Ocurre algo parecido con ciertas configuraciones de cifrado y con opciones que exigían listas de certificados, retiradas porque un error de configuración dejaba el sitio inaccesible durante meses sin forma de corregirlo.
La conclusión práctica no es memorizar cuáles están vigentes, sino tomar la referencia de una fuente que se mantenga al día y revisarla de vez en cuando. Una configuración de seguridad copiada de un artículo de hace cinco años puede estar protegiendo de amenazas que ya no existen mientras deja abiertas las actuales.
Qué se le escapa a una IA con esto
Pedir que se arreglen los problemas de CORS produce, con mucha fiabilidad, la respuesta que hace desaparecer el error: permitir todos los orígenes. Es la solución más repetida en foros y la que el modelo ha visto miles de veces.
El código funciona, el error se va y el sitio queda peor que antes. Y como no hay ningún síntoma, nadie vuelve sobre ello.
La forma de evitarlo es preguntar en otros términos: decir qué dominios concretos deben poder llamar y pedir la configuración mínima que lo permita, aclarando si hay credenciales de por medio. Con eso la respuesta cambia de un comodín a una lista.
Con CSP ocurre algo parecido: la política que se propone suele incluir las excepciones que hacen que todo funcione a la primera, y esas excepciones son justamente las que anulan su utilidad. Conviene pedir la política estricta primero, aplicarla en modo informativo y corregir el sitio para cumplirla, en lugar de relajar la política para que encaje con el sitio.