Volver al blog

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.

Cabeceras de seguridad: HSTS, CSP, X-Frame-Options y otras

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ños
  • includeSubDomains = 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 navegador
  • preload = 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 dominio
  • script-src = fuentes JS (para scripts inline especifica un hash o nonce)
  • style-src = fuentes CSS
  • img-src = imágenes
  • connect-src = AJAX, WebSocket, EventSource
  • frame-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-uri está hoy deprecated - los navegadores compatibles con report-to lo ignoran. La forma moderna es la directiva report-to más una cabecera Reporting-Endpoints aparte; deja report-uri solo como fallback para Firefox, que todavía no soporta report-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.

Probar el sitio web →

Compartir: Enlace copiado

Prueba ePulz.io gratis - 7 días sin tarjeta de crédito.

Crear cuenta