
Generador CSP: Content Security Policy sin dolores de cabeza
📷 Pixabay / PexelsGenerador CSP: Content Security Policy sin dolores de cabeza
Content Security Policy protege contra ataques XSS. Configura directivas, evita errores comunes y prueba en modo report-only antes de aplicar.
Content Security Policy (CSP) es una de las herramientas de seguridad web más efectivas contra los ataques XSS — y uno de los headers HTTP más complicados de configurar. Un error tipográfico en el nombre de una directiva no genera ningún mensaje de error. Una directiva olvidada puede dejar una brecha de seguridad abierta o tirar abajo la producción.
Esta guía explica cómo funciona la CSP, qué directivas son esenciales, qué errores comete casi todo el mundo, y cómo el generador CSP simplifica considerablemente todo el proceso.
Por qué la CSP es importante
XSS (Cross-Site Scripting) lleva años en la lista de las vulnerabilidades web más peligrosas. Un atacante inyecta JavaScript malicioso en una página web — mediante campos de entrada, parámetros de URL o contenido de terceros mal gestionado. Las consecuencias pueden ser graves:
- Robo de tokens de sesión y cookies
- Acciones realizadas en nombre del usuario
- Inserción de formularios de inicio de sesión falsos (phishing en el contexto de la página)
- Lectura de datos sensibles mostrados en la página
La validación de entradas es la primera línea de defensa. Pero las aplicaciones reales son complejas: editores rich text, integraciones de terceros, código legado... La CSP es la segunda línea de defensa — aunque una inyección tenga éxito, impide la ejecución del script malicioso.
La estructura de un header CSP
La CSP se envía como header HTTP de respuesta:
Content-Security-Policy: directiva1 valor1 valor2; directiva2 valor1; ...
Ejemplo práctico:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; img-src 'self' data: https:;
Cada directiva se separa con punto y coma. Después del nombre de la directiva van los orígenes permitidos, separados por espacios.
Las directivas CSP esenciales
default-src: el comodín de seguridad
Todo lo que no cubre una directiva más específica cae bajo default-src:
default-src 'self';
'self' solo permite recursos del mismo origen (mismo protocolo, dominio y puerto).
script-src: la directiva más crítica
Controla desde dónde se puede cargar JavaScript:
script-src 'self' https://cdn.jsdelivr.net https://www.googletagmanager.com;
Valores de fuente más comunes:
'self'— Solo mismo origen'none'— Ningún script permitido'nonce-{valor_aleatorio}'— Scripts inline con el atributo nonce coincidente'strict-dynamic'— Scripts de confianza pueden cargar otros scripts dinámicamentehttps://example.com— Origen explícitamente permitido
Evitar absolutamente en producción: 'unsafe-inline' y 'unsafe-eval'. Estos valores anulan la mayor parte de la protección XSS.
style-src: control de hojas de estilo
style-src 'self' https://fonts.googleapis.com;
img-src: control de imágenes
img-src 'self' data: https:;
data: permite imágenes inline en base64. https: permite todas las imágenes externas vía HTTPS.
font-src: control de fuentes
font-src 'self' https://fonts.gstatic.com;
connect-src: control de conexiones de red
Controla los destinos de fetch(), XMLHttpRequest, WebSockets:
connect-src 'self' https://api.example.com wss://ws.example.com;
frame-src: control de iframes
frame-src 'none'; /* iframes prohibidos */
frame-src https://www.youtube.com; /* Solo YouTube */
form-action: controla el destino de formularios
form-action 'self';
base-uri: restringe la etiqueta base
Evita que un atacante inserte una etiqueta <base> para secuestrar URLs relativas:
base-uri 'self';
frame-ancestors: quién puede incrustar la página
Reemplaza el obsoleto header X-Frame-Options:
frame-ancestors 'none'; /* La página no puede incrustarse en iframes */
frame-ancestors 'self'; /* Solo el mismo origen */
upgrade-insecure-requests: forzar HTTPS
upgrade-insecure-requests;
Todas las solicitudes HTTP se actualizan automáticamente a HTTPS.
Los errores más comunes
Error 1: recurrir a unsafe-inline por comodidad
Los scripts inline no funcionan, entonces se añade 'unsafe-inline'. Resultado: script-src prácticamente no protege nada contra XSS.
La solución correcta: nonces. El servidor genera un valor aleatorio en cada solicitud, presente tanto en el header CSP como como atributo nonce en las etiquetas script autorizadas:
<script nonce="pQ5sT8vY3kM6">
// Este script está autorizado — su nonce coincide
</script>
Header CSP:
script-src 'self' 'nonce-pQ5sT8vY3kM6';
Como el nonce cambia en cada solicitud, el atacante no puede predecirlo ni aprovecharlo.
Error 2: olvidar los scripts de terceros
Google Analytics, Stripe, Intercom, Hotjar y muchos otros servicios cargan sus scripts desde sus propios CDN — sus orígenes deben estar en script-src:
script-src 'self'
https://www.googletagmanager.com
https://www.google-analytics.com
https://js.stripe.com;
Antes de desplegar, revisa la consola del navegador (pestañas "Red" y "Consola") para detectar recursos bloqueados.
Error 3: olvidar que connect-src e img-src también necesitan ajustes
Google Analytics no solo requiere script-src, también necesita:
img-src 'self' https://www.google-analytics.com data:;
connect-src 'self' https://www.google-analytics.com;
Error 4: desplegar en producción sin pruebas previas
Lanzar una CSP en producción sin pruebas puede romper funcionalidades críticas. Un script que deja de cargarse, una fuente que desaparece, una petición de API bloqueada.
El modo report-only: pruebas sin riesgo
Usa Content-Security-Policy-Report-Only para pruebas seguras en producción:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-reportes
Este header:
- No bloquea ningún recurso (el sitio funciona con normalidad)
- Reporta todas las violaciones al endpoint
report-uri - Permite probar con tráfico real de producción
El reporte de violación llega en formato JSON:
{
"csp-report": {
"document-uri": "https://example.com/pagina",
"violated-directive": "script-src-elem",
"blocked-uri": "https://servicio-externo.com/script.js",
"original-policy": "default-src 'self'; script-src 'self'"
}
}
Usa estos reportes para ampliar progresivamente la lista blanca, hasta que desaparezcan los falsos positivos — luego cambia al header Content-Security-Policy real.
Ejemplos de configuraciones prácticas
Sitio estático simple
Content-Security-Policy:
default-src 'self';
img-src 'self' data:;
style-src 'self';
font-src 'self';
form-action 'self';
base-uri 'self';
frame-ancestors 'none';
Sitio marketing con Google Analytics
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' https://www.google-analytics.com data:;
connect-src 'self' https://www.google-analytics.com;
form-action 'self';
base-uri 'self';
Aplicación SaaS con Stripe
Content-Security-Policy:
default-src 'self';
script-src 'self' https://js.stripe.com;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https:;
connect-src 'self' https://api.stripe.com https://api.tuapp.com;
frame-src https://js.stripe.com;
form-action 'self';
base-uri 'self';
upgrade-insecure-requests;
SPA con nonces
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{nonce_generado_por_servidor}' 'strict-dynamic';
style-src 'self' 'nonce-{nonce_generado_por_servidor}';
img-src 'self' data: blob:;
connect-src 'self' https://api.tuapp.com;
font-src 'self';
form-action 'self';
base-uri 'self';
upgrade-insecure-requests;
CSP mediante etiqueta meta
Sin acceso a los headers HTTP (alojamiento estático puro):
<head>
<!-- Colocar lo antes posible en el head -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self'">
</head>
Limitaciones de la etiqueta meta:
frame-ancestorsno es compatiblereport-urino es compatible- La protección se aplica ligeramente más tarde que con headers HTTP
Los headers HTTP son preferibles cuando se tiene control sobre ellos.
Flujo de trabajo recomendado para la adopción
- Empezar en modo report-only — Recopilación de datos, sin bloqueos
- Observar las violaciones en producción — Unos días de tráfico real son suficientes
- Refinar la lista blanca — Añadir fuentes legítimas, identificar amenazas reales
- Cambiar al header CSP efectivo — Una vez estabilizada la lista blanca
- Monitorizar continuamente — Las nuevas funcionalidades pueden introducir nuevas violaciones
Por qué usar un generador
Escribir un header CSP a mano tiene varios riesgos:
- Los errores tipográficos en los nombres de directivas son silenciosos
- La sintaxis exacta (¿cuándo necesitan comillas los valores?) es fuente de errores
- Gestionar múltiples servicios de terceros en una lista blanca se vuelve tedioso rápidamente
- Alternar entre modo report-only y modo aplicado genera confusión
El generador CSP resuelve estos problemas:
- Interfaz visual para cada directiva
- Sintaxis automáticamente correcta (comillas, puntos y coma, espacios)
- Perfiles preconfigurados para servicios habituales (Google Analytics, Stripe, etc.)
- Header completo listo para copiar y pegar en la configuración del servidor
Resumen
La CSP es una capa de seguridad esencial para las aplicaciones web modernas. Los puntos clave:
- Evitar
'unsafe-inline'y'unsafe-eval'en producción — los nonces son la alternativa correcta - Probar con el modo report-only antes de aplicar la CSP
- Los scripts de terceros requieren entradas en
script-src,connect-srcy a menudoimg-src base-uri 'self'yform-action 'self'protegen contra otros vectores de ataqueframe-ancestors 'none'reemplazaX-Frame-Options- Monitorizar las violaciones de forma continua
Abre el generador CSP, configura tus directivas mediante la interfaz y copia el header generado en tu configuración de servidor — es más rápido y fiable que escribirlo a mano.