ToolPal
Candado y concepto de seguridad en un portátil

Generador CSP: Content Security Policy sin dolores de cabeza

📷 Pixabay / Pexels

Generador 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.

14 de abril de 20267 min de lectura

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ámicamente
  • https://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-ancestors no es compatible
  • report-uri no 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

  1. Empezar en modo report-only — Recopilación de datos, sin bloqueos
  2. Observar las violaciones en producción — Unos días de tráfico real son suficientes
  3. Refinar la lista blanca — Añadir fuentes legítimas, identificar amenazas reales
  4. Cambiar al header CSP efectivo — Una vez estabilizada la lista blanca
  5. 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-src y a menudo img-src
  • base-uri 'self' y form-action 'self' protegen contra otros vectores de ataque
  • frame-ancestors 'none' reemplaza X-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.

Preguntas Frecuentes

Compartir

XLinkedIn

Publicaciones relacionadas