Respuesta rápida
Security headers son la primera línea de defensa contra XSS, clickjacking, MITM y info leaks. Audit mínimo en 2026: CSP (con frame-ancestors + sin unsafe-inline), HSTS (preload + includeSubDomains), X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin, Cookies Secure + HttpOnly + SameSite=Lax, Permissions-Policy explícita. Reports de headers solos suelen ser info/low (€50-€200), pero como chain con otro bug pueden disparar la severity 2 niveles.
Tabla completa — headers que mirar
| Header | Valor recomendado | Qué previene | Severity si falta |
|---|---|---|---|
Strict-Transport-Security | max-age=31536000; includeSubDomains; preload | MITM / downgrade HTTPS | Medium |
Content-Security-Policy | default-src 'self'; frame-ancestors 'none'; ... | XSS, clickjacking, exfil | Medium-High |
X-Content-Type-Options | nosniff | MIME confusion XSS | Low |
X-Frame-Options | DENY o SAMEORIGIN | Clickjacking (legacy) | Low (si hay CSP frame-ancestors) |
Referrer-Policy | strict-origin-when-cross-origin | Token leak via Referer | Low-Medium |
Permissions-Policy | camera=(), microphone=(), geolocation=() | Abuse de APIs sensibles | Low |
Cross-Origin-Opener-Policy | same-origin | Spectre, window.opener | Low-Medium |
Cross-Origin-Embedder-Policy | require-corp | Spectre, side-channel | Low |
Cross-Origin-Resource-Policy | same-origin | Crossorigin leaks | Low |
Cache-Control (en endpoints auth) | no-store, private | Data leak via cache | Medium |
Content-Security-Policy — el header rey
CSP es el más complejo y el más impactante. Una CSP mal hecha pasa todos los XSS; una CSP bien hecha bloquea incluso XSS reflejados.
Ejemplo de CSP correcta (SPA moderna)
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-RANDOM123' https://js.stripe.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' data:;
connect-src 'self' https://api.target.com wss://ws.target.com;
frame-src https://js.stripe.com https://hooks.stripe.com;
frame-ancestors 'none';
form-action 'self';
base-uri 'self';
object-src 'none';
upgrade-insecure-requests;
report-uri /csp-report
Errores que matan la CSP
| Anti-pattern | Por qué es malo |
|---|---|
script-src 'unsafe-inline' | Permite inline <script> → cualquier XSS reflejado pasa |
script-src 'unsafe-eval' | eval(), Function(), setTimeout(string) → gadget chains |
script-src https: | Permite scripts de cualquier HTTPS → no protege nada |
script-src *.cdn.com | Si el CDN sirve user content (JSONP) → bypass instantáneo |
Sin frame-ancestors | Clickjacking |
Sin base-uri | <base> injection → cambia el origen de scripts relativos |
Sin form-action | Form hijacking en XSS to exfil credentials |
default-src 'self' sin object-src 'none' | <object data="data:..."> ejecuta Flash legacy |
[!danger] frame-ancestors NO es suficiente
frame-ancestors 'none'previene iframing tradicional, pero no previene window.open() popups con clickjacking via UI confusion. Para cerrar el vector completo: añadeCross-Origin-Opener-Policy: same-origin+ verificación dewindow.top === window.selfen JS crítico.
Test CSP
# Ver el header
curl -sI https://target.com | grep -i content-security
# Evaluator oficial Google
# Pega la CSP en https://csp-evaluator.withgoogle.com/
# Buscar bypasses según las whitelisted domains
# https://github.com/PortSwigger/csp-bypass
HSTS — el header simple pero crítico
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
| Directiva | Significado |
|---|---|
max-age=31536000 | 1 año en segundos. Tiempo que el browser fuerza HTTPS |
includeSubDomains | Aplica a todos los subdomains. Crítico para evitar attacks via subdomain takeover http |
preload | Inscripción en la HSTS preload list de Chrome/Firefox (hardcoded) |
Preload list
Inscríbete en hstspreload.org. Requisitos:
- Servir HSTS con
max-age >= 31536000,includeSubDomains,preload. - Redirect HTTP → HTTPS en root + en todos los subdomains.
- Soportar HTTPS en todos los subdomains (incluso
mail.target.com).
Cookies — los flags que cuentan
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
| Flag | Qué hace | Cuándo es crítico |
|---|---|---|
Secure | Sólo en HTTPS | Siempre. Sin esto = leak via downgrade |
HttpOnly | Inaccesible desde JS | Sesiones, auth tokens — siempre |
SameSite=Lax | No se envía en cross-site POSTs | Default sensato. Protege CSRF en mutations |
SameSite=Strict | No se envía en NINGÚN cross-site request | Para tokens de máxima sensibilidad (banking) |
SameSite=None | Se envía en cross-site (necesita Secure) | Cookies de third-party widgets (analytics, embeds) |
__Host- prefix | Fuerza Secure, Path=/, sin Domain | Defense in depth para auth |
__Secure- prefix | Fuerza Secure | Para cookies de auth/session |
[!tip] SameSite Lax vs Strict en 2026 Chrome y Firefox ya hacen
SameSite=Laxpor default si no se especifica. Pero todavía hay endpoints en frameworks viejos que setean sin SameSite explícito → reportable. Strict rompe flows de OAuth/SSO porque la cookie no llega tras redirect → Lax es el sweet spot.
Referrer-Policy — el leak silencioso
Referrer-Policy: strict-origin-when-cross-origin
Sin esta header, el browser envía el Referer completo (incluyendo path + query) a cualquier sitio externo al que linkees. Resultado: tokens de password reset, session IDs en query, OAuth codes — todos filtrados a third-parties.
| Valor | Cuándo |
|---|---|
no-referrer | Máxima privacidad. Para apps sensibles (medical, financial) |
strict-origin-when-cross-origin | Default sensato en 2026. Origin completo same-origin, sólo origin cross-origin, nada cross-origin http→https |
same-origin | Sólo same-origin envía referrer, cross-origin nada |
unsafe-url | NUNCA. Envía URL completa siempre |
Reportable
URLs con tokens en query (?reset_token=, ?session=, ?code=) + sin Referrer-Policy restrictiva → al user hacer click en link externo, token se filtra al third-party. Severity Medium si el token sigue siendo válido.
Permissions-Policy — el moderno (ex Feature-Policy)
Antes era Feature-Policy. Renamed a Permissions-Policy en 2020+ y soporta más APIs.
Permissions-Policy:
camera=(),
microphone=(),
geolocation=(),
payment=(self),
usb=(),
accelerometer=(),
gyroscope=(),
magnetometer=(),
interest-cohort=()
camera=()→ ningún origen puede acceder.camera=(self)→ sólo same-origin.camera=(self "https://trusted.com")→ same-origin + ese específico.interest-cohort=()→ opt-out de FLoC/Topics API tracking.
Reportable si la app embedded en iframe puede activar mic/camera/geolocation sin que el usuario lo espere → defense in depth.
COEP / COOP / CORP — cross-origin isolation
Necesarios para usar SharedArrayBuffer, performance.now() de alta precisión, y mitigar Spectre/Meltdown.
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin
- COOP
same-origin: tu window no comparte browsing context con popups cross-origin → cierrawindow.openerattacks. - COEP
require-corp: todos los recursos cargados deben tenerCross-Origin-Resource-Policyo CORS. Activa "cross-origin isolation". - CORP
same-origin: tus recursos sólo pueden ser cargados desde same-origin.
[!info] Cuándo activarlos Si tu app usa SharedArrayBuffer (WebAssembly, threading), necesitas COOP+COEP. Si tu app NO los usa, COOP
same-origines win gratuito para cerrar window.opener attacks. COEP rompe muchas apps third-party — implementar con cuidado.
Cómo testear todo de una
Herramientas
# CLI rápido
curl -sI https://target.com
# Online — el estándar de facto
# https://securityheaders.com/?q=target.com
# https://observatory.mozilla.org/analyze/target.com
# CSP específico
# https://csp-evaluator.withgoogle.com/
Script manual de audit
#!/bin/bash
URL="$1"
echo "=== Headers de $URL ==="
curl -sI "$URL" | grep -iE "(strict-transport|content-security|x-content-type|x-frame|referrer-policy|permissions-policy|cross-origin|set-cookie)"
echo -e "\n=== Cookies analysis ==="
curl -sI "$URL" | grep -i "set-cookie:" | while read -r line; do
name=$(echo "$line" | sed 's/.*Set-Cookie: \([^=]*\)=.*/\1/i')
flags=""
echo "$line" | grep -qi "HttpOnly" || flags+="[NO HttpOnly] "
echo "$line" | grep -qi "Secure" || flags+="[NO Secure] "
echo "$line" | grep -qi "SameSite" || flags+="[NO SameSite] "
[ -n "$flags" ] && echo "Cookie '$name': $flags"
done
Cómo reportarlo (sin ser low-info spam)
Headers sueltos casi siempre son info / low. Para que el report tenga valor:
- No mandes 10 reports separados. Un report con tabla de headers faltantes + impacto agregado.
- Demuestra impacto real. "CSP permite
unsafe-inline" no es accionable. "CSP permiteunsafe-inlineY existe un reflected XSS en /search → bypass directo" → Medium. - Chain con otro bug. Header faltante + bug funcional = severity disparada.
- Bug bounty programs específicos. Algunos (Shopify, GitHub) explícitamente dicen "Missing security headers = N/A". Lee el scope.
Template de report headers
## Summary
La aplicación carece de las siguientes security headers, exponiendo a usuarios a clickjacking, XSS amplificado y leak de tokens via Referer.
## Headers afectados
| Header | Esperado | Actual |
|---|---|---|
| CSP | `default-src 'self'; frame-ancestors 'none'` | Ausente |
| HSTS | `max-age=31536000; includeSubDomains` | `max-age=300` |
| X-Frame-Options | `DENY` | Ausente |
## Impact
- Sin CSP: cualquier XSS reflejado/stored ejecuta sin restricción.
- HSTS débil: ventana de 5 min para MITM downgrade.
- Sin X-Frame-Options: clickjacking en /transfer endpoint posible.
## PoC clickjacking
[HTML con iframe del /transfer endpoint + overlay malicioso]
Hunting checklist
-
curl -sIal target → listar todos los response headers -
securityheaders.compara grade objetivo (A+ es target) - CSP en
csp-evaluator.withgoogle.com→ buscarunsafe-inline,unsafe-eval,*whitelisted - HSTS:
max-age >= 31536000+includeSubDomains+preload - Cookies de auth:
Secure+HttpOnly+SameSite=Lax|Strict - Referrer-Policy presente y restrictiva
- X-Frame-Options o CSP
frame-ancestorsen TODAS las pages - Permissions-Policy disabling APIs no usadas
- COOP
same-originmínimo en pages auth-críticas - Cache-Control en endpoints autenticados:
no-store, private - Probar chain con XSS/CSRF/clickjacking para subir severity
Labs relacionados
Practica explotación de aplicaciones con headers mal configurados y chains de XSS → CSP bypass en labs de Security Headers.
Practica esto en un lab
Security Headers
Sigue aprendiendo · cuenta gratis
Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.
Hay un payload extra al final
Por qué `frame-ancestors 'none'` en CSP NO es suficiente para prevenir clickjacking en 2026, y la regla CSP que sí cierra todos los vectores.
5 €/mes · cancela cuando quieras