Content Security Policy (CSP): Configuración y Mejores Prácticas
Aprende qué es Content Security Policy, cómo configurar directivas CSP para prevenir XSS y controlar qué recursos puede cargar tu aplicación.
TL;DR - Resumen rápido
- Content-Security-Policy header define listas blancas con directivas como script-src 'self', style-src 'nonce-abc123'
- Bloquea scripts inline y eval() a menos que uses nonces (nonce-xyz) o hashes SHA256
- default-src 'none' bloquea todo por defecto, luego especificas qué permitir en cada directiva
- securitypolicyviolation event detecta violaciones en el navegador con blockedURI y violatedDirective
- Content-Security-Policy-Report-Only reporta violaciones sin bloquear recursos (ideal para testing)
Introducción a CSP
Content Security Policy (CSP) es un estándar de seguridad que permite a los administradores de sitios web declarar fuentes de contenido aprobadas que los navegadores pueden cargar para esa página. Con CSP, puedes controlar qué scripts, estilos, imágenes, fuentes y otros recursos pueden ejecutarse o cargarse, reduciendo significativamente el riesgo de ataques XSS y otras vulnerabilidades de inyección.
CSP funciona como una lista blanca de recursos permitidos. Cuando un navegador encuentra un recurso (script, estilo, imagen, etc.), verifica si cumple con las directivas CSP. Si el recurso no está en la lista blanca, el navegador lo bloquea y reporta una violación. Esto previene que scripts maliciosos se ejecuten, incluso si logran inyectarse en la página.
CSP vs XSS
Mientras que XSS previene la inyección de scripts mediante validación y escaping, CSP bloquea la ejecución de scripts que no estén explícitamente permitidos. CSP es una capa adicional de defensa que complementa pero no reemplaza las prácticas de prevención de XSS.
Cómo Funciona CSP
CSP funciona mediante directivas que especifican qué fuentes de contenido son permitidas para diferentes tipos de recursos. Cada directiva controla un tipo específico de recurso, y puedes configurar múltiples directivas en una sola política CSP.
- <strong>Header HTTP</strong>: El servidor envía el header Content-Security-Policy con las directivas
- <strong>Meta tag</strong>: Alternativa para configurar CSP desde el HTML (menos seguro)
- <strong>Evaluación</strong>: El navegador evalúa cada recurso contra las directivas CSP
- <strong>Bloqueo</strong>: Los recursos no permitidos se bloquean antes de cargarse
- <strong>Reporte</strong>: Las violaciones se reportan a una URL configurada
Directivas CSP
Las directivas CSP definen qué fuentes de contenido son permitidas para diferentes tipos de recursos. Entender estas directivas es fundamental para configurar una política CSP efectiva.
default-src 'self' establece que por defecto solo se permiten recursos del mismo origen. script-src 'self' 'nonce-abc123' permite scripts del mismo origen y scripts inline con el nonce específico. img-src 'self' data: https: permite imágenes del mismo origen, data URIs y cualquier origen HTTPS. object-src 'none' bloquea completamente plugins como Flash. frame-ancestors 'none' previene que tu sitio sea embebido en iframes (protección contra clickjacking).
Directiva default-src
default-src es la directiva más importante porque define la política por defecto para todos los recursos. Si configuras default-src como 'none', todos los recursos estarán bloqueados a menos que especifiques directivas específicas para cada tipo de recurso.
Implementación de CSP
La implementación de CSP puede hacerse de dos formas: mediante headers HTTP (recomendado) o mediante meta tags en el HTML. Los headers HTTP son más seguros porque no pueden ser modificados por scripts del cliente.
- <strong>Headers HTTP</strong>: Configuración en el servidor (más seguro)
- <strong>Meta tags</strong>: Configuración en el HTML (menos seguro)
- <strong>Report-only mode</strong>: Prueba la política sin bloquear recursos
- <strong>Reporte de violaciones</strong>: Recibe alertas de recursos bloqueados
- <strong>Nonce y hash</strong>: Permite scripts específicos con tokens o hashes
CSP en Headers HTTP
Configurar CSP en headers HTTP es la forma más segura de implementar Content Security Policy. Los headers se envían desde el servidor y no pueden ser modificados por scripts del cliente.
El middleware Express usa res.setHeader('Content-Security-Policy', csp) para enviar el header en cada respuesta. Los nonces deben generarse dinámicamente con crypto.getRandomValues() en cada request y pasarse al template HTML como <script nonce="{{nonce}}">. Content-Security-Policy-Report-Only permite probar la política sin bloquear recursos, solo reportando violaciones a report-uri /csp-violations.
Report-Only Mode
Usa Content-Security-Policy-Report-Only para probar tu política CSP sin bloquear recursos. El navegador reportará violaciones pero permitirá que los recursos se carguen. Esto es esencial para depurar y ajustar tu política antes de aplicarla en producción.
CSP en Meta Tag
Los meta tags permiten configurar CSP directamente en el HTML. Aunque es menos seguro que los headers HTTP, puede ser útil en situaciones donde no tienes control sobre la configuración del servidor.
El meta tag <meta http-equiv="Content-Security-Policy" content="..."> define la política CSP directamente en el HTML. Los scripts con nonce="abc123" son permitidos mientras que scripts sin nonce son bloqueados. Sin embargo, scripts maliciosos pueden remover el meta tag antes de ejecutarse, y no soporta directivas importantes como report-uri y frame-ancestors. Úsalo solo cuando no tienes acceso al servidor.
Limitaciones de Meta Tags
Los meta tags CSP tienen limitaciones importantes: no pueden usar report-uri, no pueden bloquear frame-ancestors, y scripts maliciosos pueden modificarlos. Siempre que sea posible, usa headers HTTP en lugar de meta tags.
Reporte de Violaciones
El reporte de violaciones te permite recibir alertas cuando la política CSP bloquea recursos. Esto es esencial para depurar y ajustar tu política CSP, y para detectar intentos de ataques.
El evento securitypolicyviolation se dispara en el navegador cada vez que CSP bloquea un recurso. evento.blockedURI contiene la URL del recurso bloqueado, evento.violatedDirective indica qué directiva se violó (ej: "script-src"), y evento.sourceFile + evento.lineNumber identifican dónde ocurrió. Puedes enviar estos datos a tu servidor con fetch('/csp-violations') para almacenarlos y analizar patrones de ataques.
Procesamiento de Reportes
Los reportes de violaciones deben procesarse en el servidor. Puedes almacenarlos en una base de datos para análisis, enviar alertas a tu equipo de seguridad, o usar servicios de terceros como Report URI. Analiza los reportes regularmente para identificar patrones de ataques.
Errores Comunes
Estos son los errores más frecuentes al configurar CSP y cómo evitarlos.
script-src 'unsafe-inline' 'unsafe-eval' permite scripts inline y eval(), anulando completamente la protección contra XSS. script-src * permite scripts de cualquier origen (demasiado permisivo). default-src 'none' sin otras directivas bloquea TODO (scripts, estilos, imágenes). No configurar report-uri significa que no sabrás qué violaciones ocurren. Siempre usa Content-Security-Policy-Report-Only primero para probar sin romper tu aplicación.
Evitar unsafe-inline y unsafe-eval
Las directivas 'unsafe-inline' y 'unsafe-eval' permiten scripts inline y la función eval(), lo que anula la protección de CSP contra XSS. En su lugar, usa nonces o hashes para permitir scripts específicos de forma segura.
Resumen: Content Security Policy
Conceptos principales:
- •script-src 'self' 'nonce-abc123' permite scripts del mismo origen y con nonce específico
- •default-src 'none' bloquea todo por defecto, luego especificas script-src, style-src, img-src
- •object-src 'none' bloquea plugins, frame-ancestors 'none' previene clickjacking
- •res.setHeader('Content-Security-Policy', csp) en Express envía el header HTTP
- •securitypolicyviolation event expone blockedURI, violatedDirective, sourceFile, lineNumber
Mejores prácticas:
- •Content-Security-Policy-Report-Only para probar sin bloquear recursos
- •Genera nonces dinámicos con crypto.getRandomValues() por cada request
- •Evita 'unsafe-inline' y 'unsafe-eval' - usan nonces como script-src 'nonce-xyz'
- •Configura report-uri /csp-violations para recibir reportes JSON de violaciones
- •Usa headers HTTP, no meta tags (headers no pueden ser modificados por scripts)