CSP es una de las defensas más efectivas contra ataques XSS. Al indicar al navegador qué recursos pueden cargarse, CSP previene la inyección de código malicioso.
¿Qué es CSP?
CSP es una cabecera HTTP que controla qué recursos puede cargar el navegador.
Cómo funciona CSP
El navegador bloquea recursos que violan la política:
Browser receives HTTP response:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
Browser allows:
✅ <script src="/app.js"> (same origin = 'self')
✅ <script src="https://cdn.example.com/lib.js">
Browser blocks:
❌ <script src="https://evil.com/steal.js">
❌ <script>alert('XSS')</script> (inline = not allowed)Todas las directivas CSP
| Directiva | Controla | Ejemplo |
|---|---|---|
| default-src | Fallback for all resource types | 'self' |
| script-src | JavaScript files and inline scripts | 'self' https://cdn.example.com |
| style-src | CSS stylesheets and inline styles | 'self' 'unsafe-inline' |
| img-src | Images (img, favicon, CSS background) | 'self' data: https: |
| font-src | Web fonts (@font-face) | 'self' https://fonts.gstatic.com |
| connect-src | XHR, fetch, WebSocket, EventSource | 'self' https://api.example.com |
| media-src | Audio and video elements | 'self' |
| frame-src | Iframes and embedded frames | 'self' https://www.youtube.com |
| object-src | Plugins (Flash, Java applets) | 'none' |
| base-uri | Base element URLs | 'self' |
| form-action | Form submission targets | 'self' |
| frame-ancestors | Who can embed this page (anti-clickjacking) | 'self' |
| upgrade-insecure-requests | Upgrade HTTP requests to HTTPS | (no value needed) |
Valores de origen
| Origen | Significado |
|---|---|
| 'self' | Same origin (protocol + host + port) |
| 'none' | Block all resources of this type |
| 'unsafe-inline' | Allow inline scripts/styles (reduces security!) |
| 'unsafe-eval' | Allow eval(), Function(), setTimeout(string) |
| 'strict-dynamic' | Trust scripts loaded by already-trusted scripts |
| 'nonce-abc123' | Allow specific inline script with matching nonce attribute |
| 'sha256-...' | Allow inline script matching this hash |
| https: | Allow any HTTPS resource |
| data: | Allow data: URIs (e.g., inline images) |
| blob: | Allow blob: URIs |
| *.example.com | Allow all subdomains of example.com |
Implementación paso a paso
Paso 1: Modo solo reporte
Desplegar primero en modo reporte:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-reportPaso 3: Construir política
Crear política:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com;
frame-ancestors 'self';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requestsConfiguraciones comunes
Política estricta
Content-Security-Policy:
default-src 'none';
script-src 'self';
style-src 'self';
img-src 'self';
font-src 'self';
connect-src 'self';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
object-src 'none';
upgrade-insecure-requestsModerada (SPA típica)
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com wss://ws.example.com;
frame-ancestors 'self'Con Google Analytics
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;
img-src 'self' https://www.google-analytics.com https://www.googletagmanager.com;
connect-src 'self' https://www.google-analytics.com https://analytics.google.comMétodos de implementación
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;Apache
Header set Content-Security-Policy "default-src 'self'; script-src 'self'"Express.js
// Using helmet middleware (recommended)
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
}
}));
// Or manually
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy',
"default-src 'self'; script-src 'self'");
next();
});Meta tag HTML
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'">Errores comunes
| Error | Impacto | Solución |
|---|---|---|
| unsafe-inline para scripts | Protección XSS desactivada | Usar nonces o hashes |
| unsafe-eval | Permite eval() | Refactorizar sin eval() |
| Wildcard en script-src | Scripts de cualquier dominio | Listar dominios específicos |
| Olvidar estilos inline | Bloquea CSS-in-JS | Agregar unsafe-inline a style-src |
| No probar en report-only | Rompe el sitio | Siempre empezar en report-only |
| Falta base-uri | Inyección de tag base | Agregar base-uri 'self' |
FAQ
¿Qué ataques previene CSP?
Principalmente XSS. También clickjacking e inyección de datos.
¿CSP reemplaza la validación de entrada?
No. CSP es defensa en profundidad.
¿Cómo usar CSP con scripts inline?
Usar nonces o hashes. Evitar unsafe-inline.
¿CSP ralentiza el sitio?
No. El impacto en rendimiento es despreciable.