Por qué Content Security Policy es tu primera línea de defensa
CSP no es un firewall, es una whitelist de qué recursos puede cargar tu app. Sin CSP, cualquier script inyectado (XSS) se ejecuta con los permisos de tu dominio: acceso a cookies, localStorage, puede hacer requests a tu API. Con CSP, aunque un atacante inyecte <script>alert('hack')</script>, el navegador lo bloquea si no cumple la política. Es defensa en profundidad: asumís que tu código tiene bugs, pero limitás el daño. CSP también previene clickjacking con frame-ancestors, fuerza HTTPS con upgrade-insecure-requests, y bloquea plugins obsoletos con object-src 'none'. Una policy bien configurada reduce el riesgo de XSS en 90%.
Directivas CSP esenciales y qué controlan
default-src: Fallback para todo lo que no especifiques; empezá con 'self'. script-src: De dónde pueden venir scripts; evitá 'unsafe-inline' (permite XSS), usá nonces o hashes. style-src: Idem para CSS; librerías third-party suelen necesitar 'unsafe-inline'. img-src: Fuentes de imágenes; data: permite base64, https: permite cualquier HTTPS. connect-src: APIs y WebSockets permitidos; limitá a tus dominios. frame-ancestors: Quién puede embeber tu página; 'none' previene clickjacking. base-uri: Limita <base> tag (vector de ataque raro pero real). form-action: A dónde pueden submitear forms.
Migrar a CSP sin romper tu app en producción
Empezá con Content-Security-Policy-Report-Only: loggea violaciones sin bloquear nada. Configurá report-uri /csp-violations y analizá qué recursos se cargan (Google Fonts, CDNs, analytics). Agregá dominios a la whitelist gradualmente. Común encontrar: inline styles en componentes React, event handlers inline (onclick='...'), eval() en librerías viejas. Soluciones: extraer inline styles a archivos, usar event listeners programáticos, reemplazar librerías que usan eval. Una vez que Report-Only no muestra violaciones por 1 semana, activá el header real. Usá nonces dinámicos (genera un random por request) para scripts críticos que deben ser inline.
Errores comunes al configurar CSP
Usar 'unsafe-inline' y 'unsafe-eval' por default: Anula 80% de la protección CSP; solo usá si no hay alternativa y entendés el riesgo. Olvidar 'self' en default-src: Si ponés default-src https:, tus propios scripts fallan; siempre incluí 'self'. No especificar protocol en dominios: cdn.example.com permite HTTP y HTTPS; mejor https://cdn.example.com. Whitelist demasiado amplia: script-src https: permite cualquier HTTPS, incluyendo CDNs comprometidos; sé específico. No testear en todos los navegadores: Safari tiene quirks con CSP; probá antes de producción. No versionar policies: Guardá tu CSP en git; sabés qué cambió si algo rompe.
Tabla de referencia: directivas CSP más usadas
Referencia rápida de las directivas de Content Security Policy que vas a configurar el 90% de las veces, qué recurso controla cada una y un valor de ejemplo seguro.
| Directiva | Qué controla | Valor de ejemplo |
|---|---|---|
default-src | Fallback para cualquier directiva -src que no definas | 'self' |
script-src | Orígenes válidos para JavaScript (<script>, eventos inline, eval) | 'self' 'nonce-{RANDOM}' |
style-src | Orígenes válidos para CSS y hojas de estilo | 'self' |
img-src | Orígenes válidos para imágenes y favicons | 'self' data: https: |
connect-src | Destinos de fetch, XHR, WebSocket y EventSource | 'self' https://api.example.com |
font-src | Orígenes válidos para fuentes web (@font-face) | 'self' https://fonts.gstatic.com |
frame-ancestors | Quién puede embeber tu página en un iframe (anti-clickjacking) | 'none' |
base-uri | Restringe las URLs válidas para la etiqueta <base> | 'self' |
form-action | Destinos a los que un <form> puede hacer submit | 'self' |
object-src | Orígenes para <object>, <embed> y plugins | 'none' |
Regla general: arrancá con default-src 'self', poné object-src 'none' y frame-ancestors 'none', y solo abrí orígenes concretos a medida que el modo Report-Only te lo pida.