Cabeceras de seguridad: HSTS, CSP, X-Frame-Options y otras
· 7 min de lectura
El backend es una fortaleza, pero media defensa está en el navegador. HSTS, CSP y otras cabeceras bloquean XSS, clickjacking y MITM, y son gratis.
Por qué importan
Una aplicación puede ser perfectamente segura en el backend y aun así seguir siendo vulnerable a ataques del lado del navegador: cross-site scripting (XSS), clickjacking, protocol downgrade, MIME confusion. Las cabeceras de seguridad llevan la defensa al propio navegador.
Strict-Transport-Security (HSTS)
Fuerza HTTPS para todas las visitas futuras. Tras la primera visita por HTTPS, el navegador recuerda el dominio y reescribe por sí mismo cualquier enlace http:// a https://, incluso si el usuario hace clic en un enlace incorrecto.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
max-age=63072000= válido 2 añosincludeSubDomains= la regla se aplica también a todos los subdominios; fuerza HTTPS en todas partes, de modo que un subdominio que funcione solo por HTTP, p. ej.staging.example.com, queda inaccesible en el navegadorpreload= permite la inclusión en la lista HSTS preload integrada en Chrome / Firefox / Safari
Atención:
Quitar un HSTS con preload de la lista tarda 6+ meses. No añadas preload antes de verificar que HTTPS funciona de forma estable en todos los subdominios.
Content-Security-Policy (CSP)
La cabecera más potente, pero también la más difícil de configurar. Whitelista desde dónde puede el navegador cargar scripts, estilos, imágenes e iframes. Sin CSP, un atacante que pueda inyectar una etiqueta <script> puede ejecutar cualquier JS; con CSP, solo código de fuentes aprobadas.
Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-...'; img-src 'self' data: https://cdn.example.com; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'
Directivas comunes:
default-src 'self'= por defecto, permite solo recursos de mi propio dominioscript-src= fuentes JS (para scripts inline especifica un hash o nonce)style-src= fuentes CSSimg-src= imágenesconnect-src= AJAX, WebSocket, EventSourceframe-ancestors 'none'= nadie puede incrustar la página en un iframe (mejor que X-Frame-Options)report-uri /csp-report= el navegador envía un JSON en cada violación (la capturas en el backend y la registras)- Nota:
report-uriestá hoy deprecated - los navegadores compatibles conreport-tolo ignoran. La forma moderna es la directivareport-tomás una cabeceraReporting-Endpointsaparte; dejareport-urisolo como fallback para Firefox, que todavía no soportareport-to.
X-Frame-Options
Alternativa más antigua a frame-ancestors. Previene el clickjacking, en el que el atacante incrusta tu página como iframe y la cubre con botones invisibles.
X-Frame-Options: DENY
Valores: DENY (nadie), SAMEORIGIN (solo mi dominio), ALLOW-FROM uri (deprecated).
X-Content-Type-Options
X-Content-Type-Options: nosniff
Desactiva el MIME sniffing, de modo que el navegador respeta el Content-Type que devolvió el servidor. Sin esto, un atacante puede subir un archivo con tipo incorrecto (p. ej. una imagen que en realidad es HTML con script) y el navegador puede ejecutarlo como una página web.
Referrer-Policy
Referrer-Policy: strict-origin-when-cross-origin
Controla cuánta información sobre la página anterior envía el navegador en la cabecera Referer al hacer clic. El valor por defecto en navegadores modernos ya es strict-origin-when-cross-origin, pero fijar la cabecera de forma explícita garantiza un comportamiento coherente.
Permissions-Policy
Desactiva las API que no necesitas, como cámara, micrófono, GPS y geolocalización. Un atacante que explote XSS no puede solicitar estas API.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Configuración práctica en nginx
server {
listen 443 ssl http2;
server_name example.com;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
# hemos omitido preload aquí a propósito - añádelo solo cuando estés 100% seguro (ver la trampa de preload arriba)
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# CSP es extensa - defínela según tu aplicación
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; ..." always;
}
La clave es always - sin él, nginx omite las cabeceras en las respuestas de error (4xx, 5xx).
Auditoría del estado actual
Lo más fácil es una herramienta online. Nuestro header check te mostrará qué cabeceras faltan y cuáles son incorrectas. Para una auditoría exhaustiva, securityheaders.com también da una puntuación A-F.
Conclusión
Las cabeceras de seguridad son una de las mejores inversiones en seguridad por su relación esfuerzo-beneficio: normalmente media hora de configuración en un reverse proxy contra toda una categoría de ataques del lado del navegador. Una auditoría una vez cada medio año como parte del mantenimiento regular es un mínimo razonable.
Auditoría gratuita de security headers
Sin registro, resultado en tres segundos.
Prueba ePulz.io gratis - 7 días sin tarjeta de crédito.
Crear cuenta