Pentest

Cabeceras de seguridad HTTP: qué hace cada una

Las cabeceras de seguridad son instrucciones que tu servidor le manda al navegador sobre cómo tratar la página: qué puede cargar, quién puede incrustarla, si tiene que usar siempre cifrado. Cuestan poco, se configuran en un solo lugar y recortan categorías enteras de problemas. Eso sí, no arreglan una aplicación con fallas, y mal activadas pueden romper el sitio. Este repaso va de las más útiles a las más delicadas.

Strict-Transport-Security

Strict-Transport-Security (HSTS) le dice al navegador que, para ese dominio, use siempre HTTPS durante un período determinado, incluso si el usuario escribe http://. Evita que alguien en la red intermedia degrade la primera conexión.

El error típico es activarla con un plazo larguísimo antes de verificar que todos los subdominios responden bien por HTTPS. Si extendés la política a subdominios y alguno solo funciona sin cifrado, queda inaccesible hasta que venza el plazo. Empezá con un valor corto, comprobá, y recién después subilo.

Content-Security-Policy

Content-Security-Policy (CSP) es la más potente y la más trabajosa. Declara desde qué orígenes puede cargarse cada tipo de recurso: scripts, estilos, imágenes, conexiones. Su valor principal es limitar el daño de una inyección de scripts (XSS): aunque alguien logre colar una etiqueta, el navegador se niega a ejecutar lo que no está permitido.

Las políticas que incluyen unsafe-inline o comodines muy amplios en los scripts pierden casi toda su utilidad. Lo ideal es evitar scripts en línea y, si hace falta alguno, usar un valor aleatorio por respuesta (nonce) o un hash.

Para activarla sin romper nada, existe el modo solo informe, Content-Security-Policy-Report-Only. El navegador no bloquea, pero reporta qué habría bloqueado. Dejalo corriendo un tiempo, ajustá la política con lo que aparezca y después pasá a modo de aplicación.

X-Content-Type-Options

Con el valor nosniff impide que el navegador adivine el tipo de un archivo ignorando el Content-Type declarado. Sin esto, un archivo subido por un usuario y servido con un tipo equivocado podría interpretarse como script. Es de las más baratas: casi nunca rompe nada, siempre que tus tipos de contenido sean correctos.

frame-ancestors y X-Frame-Options

Controlan quién puede incrustar tu página dentro de un iframe, lo que protege contra el clickjacking, esa técnica donde se superpone tu página invisible sobre otra para engañar un clic. La forma moderna es la directiva frame-ancestors dentro de la CSP. X-Frame-Options cumple la misma función y se mantiene por compatibilidad con navegadores más viejos. Si tu sitio no tiene razón para ser incrustado, prohibilo del todo; si lo incrusta un socio, listá su origen explícitamente.

Referrer-Policy

Cuando alguien sigue un enlace, el navegador puede enviar la dirección de origen. Si esa dirección lleva datos sensibles en la ruta o la consulta (un identificador de sesión, un token de recuperación), terminan en los registros de un tercero. Referrer-Policy con un valor restrictivo, como strict-origin-when-cross-origin, manda solo el origen hacia afuera y la ruta completa dentro del propio sitio. De todas formas, la solución real es no poner secretos en las URL.

Permissions-Policy

Permite deshabilitar funciones del navegador que tu sitio no usa, como cámara, micrófono o geolocalización, o limitarlas a tu propio origen. Reduce lo que podría abusar un script de terceros o un contenido incrustado. Las funciones soportadas cambian con el tiempo, así que revisá la documentación vigente de los navegadores que te importan.

Un bloque de ejemplo

Esta es una configuración genérica para un servidor nginx. Es un punto de partida para ajustar, no una receta final:

add_header Strict-Transport-Security "max-age=300" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy-Report-Only "default-src 'self'; frame-ancestors 'none'" always;

El max-age de cinco minutos es a propósito: se sube a medida que ganás confianza. Y la CSP está en modo informe hasta que revises qué reporta. Una advertencia sobre add_header en nginx: se hereda entre niveles de configuración solo si el bloque interior no define ninguna propia, así que si agregás una cabecera en un location, repetí las demás o vas a perderlas sin darte cuenta.

Qué no resuelven

Ninguna de estas cabeceras corrige una consulta SQL armada con texto concatenado, un control de acceso ausente o una contraseña débil. Son una segunda línea de defensa. Tampoco hay que activarlas todas de golpe un viernes: probá en un entorno de pruebas, hacé el cambio de a una cabecera, mirá la consola del navegador y los reportes, y verificá el resultado desde afuera con una herramienta de análisis de cabeceras o con un simple curl -I contra tu propio dominio.