Saltar al contenido principal

Cabeceras de seguridad HTTP

Las cabeceras HTTP de seguridad son instrucciones que el servidor envía al navegador junto con la respuesta para indicarle cómo debe comportarse al manejar el contenido de la página. Establecen una capa de protección adicional frente a ataques como XSS, clickjacking, inyección de contenido, etc.

Son una de las medidas más fáciles y eficaces de implementar, pero también de las más olvidadas.

Principales cabeceras de seguridad

Content-Security-Policy (CSP)

Es la cabecera de seguridad más potente. Define qué recursos (scripts, estilos, imágenes, fuentes, etc.) puede cargar el navegador y desde qué orígenes. Si un script intenta ejecutarse desde un origen no autorizado, el navegador lo bloquea.

Protege contra:

  • XSS
  • Inyección de contenido

Ejemplo:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.miweb.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none'

Directivas más importantes de Content-Security-Policy:

DirectivaQué controla
default-srcPolítica por defecto para todos los recursos
script-srcFuentes de JavaScript
style-srcFuentes de CSS
img-srcFuentes de imágenes
connect-srcOrígenes permitidos en fetch, XHR, WebSocket
font-srcFuentes tipográficas
frame-ancestorsQué páginas pueden embeber esta en un iframe
form-actionDónde pueden enviarse los formularios

Valores especiales:

ValorSignificado
'self'Solo el propio dominio
'none'Ningún origen
'unsafe-inline'Permite código inline (evitar si es posible)
'unsafe-eval'Permite eval() (evitar)
'nonce-<valor>'Permite scripts con ese nonce específico

En Express con Helmet:

import helmet from "helmet";

app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.jsdelivr.net"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.miweb.com"],
frameAncestors: ["'none'"],
formAction: ["'self'"],
}
}));
Utilidad

CSP Evaluator es una herramienta útil para generar y probar tu CSP.

Strict-Transport-Security (HSTS)

Indica al navegador que siempre use HTTPS para comunicarse con este dominio, incluso si el usuario escribe http://. Una vez recibida, el navegador no permitirá conexiones HTTP durante el periodo indicado.

Protege contra:

  • Ataques Man-in-the-Middle
  • Downgrade attacks
  • SSL stripping

Ejemplo:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
ParámetroDescripción
max-ageSegundos que el navegador recuerda usar HTTPS (31536000 = 1 año)
includeSubDomainsAplica también a todos los subdominios
preloadSolicita inclusión en la lista HSTS preload de los navegadores
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true
}));
Importante

Activar HSTS sólo cuando el certificado SSL esté correctamente configurado. Una vez activado con preload, es muy difícil volver atrás.

X-Content-Type-Options

Impide que el navegador intente "adivinar" el tipo MIME de un recurso cuando el servidor ya lo ha declarado. Sin esta cabecera, algunos navegadores pueden ejecutar un fichero .txt como JavaScript si su contenido parece código.

Protege contra:

  • MIME-type sniffing.
  • Ataques de confusión de tipos.

Ejemplo:

X-Content-Type-Options: nosniff
app.use(helmet.noSniff()); // Incluido en helmet() por defecto

X-Frame-Options

Controla si la página puede ser incrustada en un <iframe>, <frame> o <embed>.

Protege contra:

  • Ataques de clickjacking, donde un atacante superpone un iframe invisible sobre su página para robar clics del usuario.

Ejemplo:

X-Frame-Options: DENY          # Nadie puede embeber esta página
X-Frame-Options: SAMEORIGIN # Solo el mismo dominio puede embeber
app.use(helmet.frameguard({ action: 'deny' }));
info

La directiva frame-ancestors en CSP es la alternativa moderna y más flexible. Se recomienda usar ambas para mayor compatibilidad.

Referrer-Policy

Controla qué información se incluye en la cabecera Referer al navegar a otro sitio. Por defecto, los navegadores pueden enviar la URL completa de la página origen, lo que puede filtrar información sensible (tokens en URL, rutas internas…).

Protege contra filtrado de información sensible en URLs.

Referrer-Policy: no-referrer-when-downgrade         # Por defecto en muchos navegadores
Referrer-Policy: strict-origin-when-cross-origin # Recomendado
Referrer-Policy: no-referrer # Nunca envía el referrer
app.use(helmet.referrerPolicy({
policy: 'strict-origin-when-cross-origin'
}));

Permissions-Policy (antes Feature-Policy)

Define qué APIs del navegador puede usar la página (cámara, micrófono, geolocalización, notificaciones…). Evita que scripts de terceros accedan a funcionalidades sensibles.

Protege contra el abuso de APIs del navegador por parte de scripts de terceros.

Permissions-Policy: camera=(), microphone=(), geolocation=(self), payment=()
app.use(
helmet.permittedCrossDomainPolicies(),
);

// O manualmente:
app.use((req, res, next) => {
res.setHeader(
'Permissions-Policy',
'camera=(), microphone=(), geolocation=(self)'
);
next();
});

Cross-Origin-Opener-Policy (COOP)

Aísla el contexto de navegación de la página frente a ventanas abiertas desde otros orígenes, previniendo que páginas maliciosas abiertas desde la tuya puedan acceder a tu window.

Cross-Origin-Opener-Policy: same-origin

Cross-Origin-Resource-Policy (CORP)

Indica qué orígenes pueden cargar los recursos de esta respuesta. Evita que otros sitios puedan cargar tus imágenes, scripts u otros recursos.

Cross-Origin-Resource-Policy: same-origin
Cross-Origin-Resource-Policy: same-site
Cross-Origin-Resource-Policy: cross-origin # Permite cualquier origen

Helmet: todas las cabeceras de un vistazo

Helmet es el middleware de facto para gestionar cabeceras de seguridad en Express. Con una sola línea activa las cabeceras más importantes con valores seguros por defecto.

npm install helmet
import express from 'express';
import helmet from "helmet";

const app = express();

// Configuración recomendada para producción
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
frameAncestors: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
frameguard: { action: 'deny' },
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
}));

Cabeceras que activa Helmet por defecto:

Content-Security-Policy: default-src 'self'; ...
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
X-Content-Type-Options: nosniff
X-DNS-Prefetch-Control: off
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-XSS-Protection: 0

Verificar las cabeceras de tu aplicación

Puedes comprobar qué cabeceras de seguridad tiene tu aplicación con estas herramientas:

  • securityheaders.com — Análisis y puntuación de cabeceras.
  • observatory.mozilla.org — Análisis completo de seguridad web de Mozilla.
  • Herramientas de desarrollador del navegador — Pestaña Network → selecciona una petición → Response Headers.