
La seguridad web es crítica en el desarrollo moderno, y Content Security Policy (CSP) se ha convertido en una herramienta esencial para proteger las aplicaciones. CSP es un mecanismo de seguridad que permite a un sitio web declarar de forma explícita qué contenido es seguro de cargar, reduciendo drásticamente el riesgo de ejecuciones de código malicioso en la página.
Al definir una política CSP adecuada, podemos prevenir ataques comunes como Cross-Site Scripting (XSS) y otros vectores de inyección de código en el navegador.
En este artículo te explico qué es CSP, sus beneficios en la seguridad web (especialmente cómo mitiga ataques XSS) y luego te presentaré una guía paso a paso para implementar CSP en distintos contextos: sitios estáticos, aplicaciones React, aplicaciones Angular, aplicaciones Next.js (incluyendo SSR) y sitios WordPress. Y por último, te mostraré ejemplos de configuración (vía etiqueta <meta> y cabeceras HTTP), el uso del modo Report-Only y recomendaciones para crear una CSP segura sin romper la funcionalidad de tu sitio.
Índice de Contenidos
¿Qué es Content Security Policy (CSP)?
Content Security Policy (CSP) es un estándar de seguridad web que permite a los desarrolladores especificar qué fuentes de contenido son confiables en una aplicación web. En esencia, CSP actúa como una lista blanca de recursos permitidos: el navegador, al cargar la página, solo permitirá ejecutar o cargar aquellos recursos (scripts, hojas de estilo, imágenes, fuentes, frames, etc.) provenientes de orígenes explícitamente autorizados por la política, bloqueando todo lo demás. Esto brinda un control fino sobre qué contenido puede mostrarse o ejecutarse, fortaleciendo la protección contra código inyectado de forma maliciosa.
Una política CSP se transmite típicamente mediante una cabecera HTTP llamada Content-Security-Policy en las respuestas del servidor. Alternativamente, si no se puede configurar la cabecera en el servidor, la política puede declararse dentro del HTML usando una etiqueta <meta http-equiv=»Content-Security-Policy» …> en la sección <head>. Cuando el navegador ve esta política, la aplica a todos los contenidos de la página. La sintaxis de CSP consiste en una serie de directivas separadas por punto y coma, donde cada directiva define qué orígenes están permitidos para un tipo de recurso.
Por ejemplo, una política sencilla podría ser:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.ejemplo.com; style-src 'self' 'unsafe-inline'; object-src 'none';" />
En este ejemplo de CSP:
- default-src ‘self’ establece que, por defecto, todos los recursos (que no tengan una directiva más específica) deben provenir del mismo origen del sitio (‘self’ refiere al dominio propio.
- script-src ‘self’ https://cdn.ejemplo.com permite cargar scripts solo desde el propio sitio y desde cdn.ejemplo.com. Cualquier <script> de otro dominio será bloqueado por el navegador.
- style-src ‘self’ ‘unsafe-inline’ permite estilos (CSS) del propio sitio y también estilos inline. El valor ‘unsafe-inline’ se usa aquí para permitir estilos en línea (por ejemplo, atributos style=»» en HTML o estilos inyectados por scripts); sin esta opción, cualquier CSS inline sería bloqueado. (Más adelante discutiremos por qué conviene evitar unsafe-inline en la medida de lo posible).
- object-src ‘none’ deshabilita la carga de objetos externos (como <object>, <embed> o plugins tipo Flash), denegándolos por completo.
Una política CSP bien configurada, como la anterior, asegura que el navegador ignore cualquier recurso que no coincida con las fuentes permitidas. Si un atacante intenta inyectar un script de origen no autorizado o ejecutar código inline no permitido, el navegador lo bloqueará gracias a CSP.
Beneficios de CSP en la seguridad web (mitigación de XSS y otras amenazas)
La principal motivación para implementar CSP es fortalecer la seguridad del lado del cliente. Al restringir las fuentes de contenido, CSP mitiga varios tipos de ataques web, en especial el Cross-Site Scripting (XSS).
En un ataque XSS, un actor malicioso consigue que su código JavaScript se ejecute en el contexto de tu sitio (por ejemplo, inyectando un <script> mediante una vulnerabilidad de inyección). Con CSP habilitado, aunque un atacante logre insertar un script, el navegador impedirá su ejecución si el origen de ese script no está en la política. Por ejemplo, sin CSP un script inyectado podría cargar código desde evil.com/malware.js y robar datos del usuario; con una política CSP que limite script-src a fuentes de confianza (p. ej. ‘self’ y dominios aprobados), ese intento sería bloqueado de inmediato.
Además de XSS, CSP ayuda a mitigar otras amenazas comunes en aplicaciones web:
- Inyecciones de código y HTML: Cualquier intento de cargar recursos externos no autorizados (scripts, iframes, imágenes) o ejecutar código inline malicioso será abortado por el navegador. Esto dificulta ataques de inyección de contenido y reduce la superficie de explotación.
- Clickjacking: Si bien existe una cabecera específica (X-Frame-Options o la directiva CSP frame-ancestors) para prevenir clickjacking, CSP puede contribuir estableciendo frame-ancestors ‘none’ o limitando qué orígenes pueden enmarcar tu sitio.
- Desfiguración de contenido: CSP puede impedir que se muestren recursos alterados por atacantes. Por ejemplo, si alguien logra cambiar la URL de una imagen en tu página por otra hostil, la política img-src apropiada evitará que imágenes de orígenes no permitidos se carguen.
- Carga forzada de contenido inseguro: Con CSP es posible requerir que todos los recursos se carguen por HTTPS (upgrade-insecure-requests) y así evitar contenido mixto.
En resumen, CSP añade una capa de defensa en profundidad en el navegador, deteniendo muchos ataques en el cliente aunque estos hayan burlado otras defensas. Es importante destacar que CSP no reemplaza la necesidad de escribir código seguro (por ejemplo, seguir validando/escapando entradas para prevenir XSS en primer lugar), pero sirve como un valioso último escudo que limita el impacto de vulnerabilidades residuales.
De hecho, organizaciones y guías de seguridad (OWASP, Mozilla, Google, etc.) recomiendan encarecidamente usar CSP como parte de una estrategia integral de seguridad web.
¿Cuál es el costo de estos beneficios?
Principalmente, CSP requiere un análisis cuidadoso de qué contenidos legítimos necesita tu sitio y ajustarlos en la política. Una política demasiado permisiva (por ejemplo, con comodines * que permiten cualquier origen, o permitiendo indiscriminadamente ‘unsafe-inline’) pierde eficacia y podría dejar pasar ataques.
Por otro lado, una CSP muy estricta, si no está bien ajustada, puede romper funcionalidad de tu propia web al bloquear recursos necesarios. Por ello, implementar CSP conlleva encontrar un balance: ser lo suficientemente restrictivo para bloquear amenazas, pero a la vez suficientemente permisivo para no interferir con las características legítimas de tu sitio. Más adelante veremos estrategias (como el modo Report-Only) para lograr esta calibración de forma segura.
A continuación, te comparto una guía práctica de implementación de CSP en distintos entornos y frameworks web populares. Veremos caso por caso cómo aplicar CSP en sitios estáticos, aplicaciones React, Angular, Next.js y WordPress, con pasos específicos y ejemplos de código de configuración (tanto usando la cabecera HTTP como la etiqueta meta). Al final, incluimos recomendaciones generales para probar la política (Report-Only) y afinarla sin causar problemas en tu sitio.
Implementación de CSP en sitios web estáticos (HTML/CSS/JS puros)
En un sitio web estático (por ejemplo, un conjunto de archivos HTML, CSS y JS servidos desde un servidor sin lógica backend), implementar CSP se reduce a configurar correctamente la política en las páginas entregadas. Hay dos formas principales de aplicar CSP en este contexto:
Mediante cabecera HTTP en el servidor – Si tienes acceso a la configuración del servidor web que hospeda tu sitio (por ejemplo, Apache, Nginx, IIS, etc.), esta es la forma más robusta. Consiste en enviar la política CSP como una cabecera HTTP Content-Security-Policy en todas las respuestas. Por ejemplo, en Apache podrías añadir esto al archivo .htaccess o configuración del host virtual:
<IfModule mod_headers.c>
Header set Content-Security-Policy "default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self';"
</IfModule>
Este fragmento instruye a Apache para incluir la cabecera CSP con la política especificada en todas las respuestas. En Nginx, una configuración equivalente sería usar la directiva add_header, por ejemplo:
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self';";
- (Asegúrate de recargar la configuración del servidor tras estos cambios). Una vez implementado, puedes verificar que la cabecera se envía correctamente abriendo las herramientas de desarrollador del navegador en la pestaña “Network” y comprobando la respuesta de tu página: deberías ver la cabecera Content-Security-Policy con el valor configurado.
Mediante etiqueta <meta> en el HTML – Si no es posible configurar el servidor (por ejemplo, estás usando un hosting estático donde no tienes acceso a las cabeceras, o simplemente prefieres definir CSP en el propio HTML), puedes usar una etiqueta meta. Esta etiqueta debe incluirse en el <head> de cada página HTML para la que quieras aplicar CSP. Por ejemplo, agrega algo como esto en la sección <head> de tu documento HTML:
<head>
<!-- ... otras metas ... -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' data:; script-src 'self' https://apis.google.com; style-src 'self' 'unsafe-inline';" />
<!-- ... tus enlaces CSS, scripts, etc ... -->
</head>
- Esta meta declara una política CSP directamente en el HTML. Es crucial insertarla antes de cualquier <script> o contenido activo en el <head>, para que el navegador la procese a tiempo y aplique las restricciones a los recursos que le siguen. Ten en cuenta que algunas funcionalidades de CSP (como ciertas directivas de reporte) pueden estar mejor soportadas vía cabecera HTTP, pero en general usar la etiqueta meta es aceptado por los navegadores modernos cuando no se puede usar la cabecera.
Al implementar CSP en un sitio estático, sigue estos pasos prácticos:
- Identifica los recursos externos que utiliza tu sitio: por ejemplo, fuentes web de Google Fonts, scripts de Google Analytics, mapas de Google Maps, etc. Deberás enumerar cada dominio necesario en la directiva CSP correspondiente (script-src para scripts, style-src para CSS, img-src para imágenes, font-src para fuentes, connect-src para peticiones AJAX, etc.). Todo lo que no esté listado será bloqueado. Si tu sitio es completamente autosuficiente (carga todo desde su propio dominio) bastará con ‘self’ en la mayoría de directivas.
- Especifica una política inicial: comienza con una política restrictiva que permita solo lo indispensable. Por ejemplo, en la configuración de arriba permitimos scripts solo de ‘self’ (el propio sitio) y de apis.google.com (imaginando que el sitio usa, digamos, un servicio de login de Google). Cualquier script de otro origen sería bloqueado. Igualmente, se permitió img-src ‘self’ data: suponiendo que el sitio podría tener imágenes en base64 embebidas (data URI) además de las del mismo dominio, y style-src ‘self’ ‘unsafe-inline’ para admitir estilos propios y estilos inline (si tu sitio no usa estilos inline, podrías omitir ‘unsafe-inline’ en estilos para mayor seguridad).
- Prueba en modo Report-Only: antes de activar la política de forma definitiva, es muy recomendable ensayarla en modo reporte. Esto implica enviar la misma política pero con la cabecera Content-Security-Policy-Report-Only en lugar de la definitiva. Con ello, el navegador no bloqueará los contenidos violatorios, pero registrará en la consola (y opcionalmente enviará reportes) cada potencial violación.
Monitorea la consola de tu navegador o utiliza herramientas de reporte (ver la sección de Report-Only más adelante) para ver qué recursos habría bloqueado la CSP. Ajusta la política según sea necesario (agregando orígenes permitidos para recursos legítimos que estén siendo reportados como bloqueados). - Activa CSP definitivamente: una vez que la política no genere violaciones significativas en modo reporte (es decir, ya has afinado la lista de fuentes permitidas), procede a usar la cabecera real Content-Security-Policy o la meta tag (quitando el Report-Only). A partir de entonces, el navegador aplicará la política en modo enforcement, bloqueando efectivamente cualquier recurso fuera de la política.
Ejemplo completo: Supongamos un sitio estático que usa jQuery desde un CDN y fuentes de Google Fonts. Una posible CSP en la cabecera HTTP podría ser:
Content-Security-Policy: default-src 'self'; script-src 'self' https://code.jquery.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; object-src 'none';
Esta política permite cargar scripts del propio sitio y de code.jquery.com, estilos del propio sitio (incluyendo inline) y de fonts.googleapis.com, fuentes desde fonts.gstatic.com, y bloquea cualquier objeto/plug-in externo. Con esta configuración, se mitigan XSS (no se aceptan scripts de orígenes no listados ni inline), se evita la carga de contenido no autorizado y se sigue permitiendo el funcionamiento de los recursos externos necesarios.
Implementación de CSP en aplicaciones React
Las aplicaciones React suelen ser single-page applications (SPA) construidas con HTML, JS y CSS que se entregan al navegador normalmente como archivos estáticos tras un proceso de build. Aun así, necesitan CSP para protegerse de inyecciones de código en el cliente. Veamos cómo aplicarla:
1. Añadir CSP en la página HTML principal (public/index.html): Si tu aplicación React fue iniciada con herramientas como Create React App (CRA), tendrás un archivo public/index.html que actúa como plantilla base. Puedes editar este archivo para incluir una metaetiqueta CSP en el <head> tal como en el caso estático. Por ejemplo, abre public/index.html y agrega:
<head>
...
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://stackpath.bootstrapcdn.com; style-src 'self' 'unsafe-inline'; frame-ancestors 'none';" />
</head>
(El contenido de la política es solo un ejemplo; debes ajustarlo a las necesidades reales de tu app.) Esta meta tag asegurará que cuando la aplicación React se cargue, el navegador aplique la CSP definida. Al igual que antes, confirma que la meta esté declarada antes de cargar cualquier script en el HTML.
2. Configurar CSP vía servidor (si aplica): Muchas aplicaciones React puras se despliegan en hosts estáticos (por ejemplo GitHub Pages, Netlify, Vercel static, etc.) donde usar la meta tag es lo más directo. Sin embargo, si tu aplicación React se sirve mediante un servidor Node.js propio (por ejemplo, usando Express para servir los archivos estáticos) o cualquier otro servidor, es preferible configurar la cabecera HTTP. En un servidor Node/Express podrías usar el paquete de middleware Helmet para este fin. Helmet simplifica añadir cabeceras de seguridad, incluyendo CSP. Un ejemplo mínimo en Express:
const express = require('express');
const helmet = require('helmet');
const app = express();
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://stackpath.bootstrapcdn.com"], // ej. permitir CDN específico
styleSrc: ["'self'", "'unsafe-inline'"],
objectSrc: ["'none'"],
// ... otras directivas según necesidad ...
}
}
}));
app.use(express.static('build'));
...
Este código configura la cabecera CSP cuando Express sirve las respuestas, similar a escribir la cabecera manualmente. Si tu React app se despliega en un servidor Apache/Nginx, igualmente puedes añadir la directiva CSP en la configuración del servidor como vimos en la sección de sitios estáticos.
3. Considera los recursos de tu app React: Por defecto, el bundle de React (los archivos JS/CSS generados) se servirá desde tu propio dominio, así que ‘self’ suele bastar. React típicamente no requiere ejecutar inline scripts (el código está en los bundles externos), por lo que es viable no incluir ‘unsafe-inline’ en script-src (mejorando la seguridad).
Sin embargo, algunas herramientas o librerías integradas en tu HTML podrían necesitarlo. Por ejemplo, CRA a veces inserta un pequeño script inline de inicialización; si descubres errores CSP en la consola relacionados a código inline bloqueado, podrías optar por permitir ‘unsafe-inline’ solo después de evaluar el impacto. Una alternativa más segura es usar nonces o hashes para esos fragmentos inline, pero en React SPA suele ser más sencillo eliminar la necesidad de inline scripts.
4. Pruebas y ajuste: Igual que en el caso estático, prueba inicialmente en modo Report-Only. Al desplegar tu aplicación React, observa en la consola del navegador si CSP está bloqueando algo necesario. Un caso común es incluir recursos de terceros: por ejemplo, si tu app carga Google Fonts o un script de analítica, tendrás que agregar los dominios correspondientes en la política o esos recursos serán bloqueados. Actualiza la política CSP en la meta o servidor hasta que la app funcione correctamente sin violaciones.
Nota: React en sí no requiere configuraciones especiales para CSP; al ser una biblioteca de UI, la responsabilidad de CSP recae en cómo sirves la aplicación. Eso sí, al usar frameworks complementarios (por ejemplo Next.js para SSR de React, que cubriremos en breve), habrá consideraciones adicionales como manejar nonces para scripts del lado del servidor. Pero en el caso de una SPA React estándar, los pasos anteriores cubren la implementación de CSP.
Implementación de CSP en aplicaciones Angular
Las aplicaciones Angular (Angular 2+ en adelante) comparten similitudes con React en cuanto a despliegue (normalmente se sirven como páginas estáticas tras el build de producción, a menos que uses Angular Universal para renderizado del lado servidor). Implementar CSP en Angular se hace, en gran parte, de la misma manera:
1. Usar la metaetiqueta CSP en index.html: Abre el archivo src/index.html de tu proyecto Angular (el archivo HTML raíz que Angular CLI genera) y agrega una meta Content-Security-Policy en el <head> con las directivas adecuadas. Ejemplo:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; base-uri 'self'; object-src 'none';" />
Este ejemplo ilustra algo importante en Angular: se ha incluido ‘unsafe-eval’ en script-src. ¿Por qué? Algunas versiones de Angular y sus dependencias (por ejemplo, el polyfill de Zone.js o ciertas funciones de Angular en modo desarrollo) utilizan construcciones de JavaScript que son consideradas «evaluación de código» (eval o new Function(…)). En una CSP estricta, eso está prohibido a menos que se permita explícitamente ‘unsafe-eval’. Si ejecutas una app Angular sin ‘unsafe-eval’, podrías obtener errores en la consola donde el navegador rechaza evaluar código por la CSP.
En aplicaciones Angular modernas compiladas en modo producción AOT, este problema se minimiza, pero conviene ser precavido. Por eso, en este ejemplo ‘unsafe-eval’ se incluye para evitar bloquear funcionalidades internas de Angular. (Si sabes con certeza que tu bundle Angular no lo necesita, puedes omitirlo; de lo contrario, mantenlo para no romper la app).
En cuanto a ‘unsafe-inline’ en style-src, se incluye porque Angular a veces inyecta estilos en tiempo de ejecución (por ejemplo, estilos component-scoped o ciertas partes del framework). Permitir estilos inline garantiza que tales inserciones de CSS no sean bloqueadas. Por otro lado, no se incluyó ‘unsafe-inline’ en script-src, ya que Angular no debería requerir scripts inline para funcionar (y mantenerlo fuera brinda más seguridad).
2. Configurar cabecera CSP en el servidor: Si tu Angular app se sirve desde un servidor web tradicional, aplica la misma lógica que en casos anteriores: configura la cabecera Content-Security-Policy con la política elegida. Esto podría hacerse en un archivo de configuración del servidor o mediante un middleware si usas Node.js/Express para servir la app Angular Universal. Por ejemplo, en un servidor Apache:
Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; object-src 'none';"
(Ajusta las directivas según tus necesidades reales, especialmente listando cualquier dominio externo necesario.)
3. Identificar directivas especiales: Además de ‘unsafe-eval’, revisa si tu Angular app usa recursos externos que deban permitirse. Por ejemplo, si usas Google Maps en un componente, necesitarás añadir script-src https://maps.googleapis.com y quizás permisos en img-src y connect-src. Angular por sí mismo no carga recursos externos inesperados, así que la política puede ser tan estricta como tus dependencias lo permitan.
4. Aprovechar mejoras de Angular para CSP: El ecosistema Angular ha reconocido históricamente las dificultades con CSP y ha ido introduciendo mejoras. En versiones recientes, Angular soporta asignar nonce automáticamente a sus scripts internos de bootstrap y estilo, facilitando la adopción de CSP estrictas. Incluso, a partir de Angular 16+, existe una opción experimental para habilitar CSP basado en hashes automáticamente en la build, de modo que el propio CLI genere los hashes de sus scripts inline y los incluya en una meta CSP.
Si estás usando Angular v16+ conviene revisar su documentación sobre CSP_NONCE y la configuración security.autoCSP en angular.json, ya que pueden ahorrarte tener que usar ‘unsafe-inline’ o ‘unsafe-eval’. Estas características avanzadas permiten que Angular se ejecute bajo una CSP muy estricta (sin permitir inline ni eval) al confiar en nonces/hashes generados automáticamente.
5. Pruebas y iteración: Como siempre, prueba la aplicación Angular con la CSP en modo Report-Only inicialmente. Observa la consola del navegador o registros de reporte para ver si algo legítimo está siendo bloqueado. Un error típico sería olvidar añadir ‘unsafe-eval’ y ver la app fallar al arrancar con errores de CSP; o quizás un servicio de terceros no considerado. Ajusta la política hasta que no queden violaciones necesarias antes de hacerla obligatoria.
En resumen, implementar CSP en Angular requiere un poco de atención extra a ciertos detalles del framework (especialmente el tema unsafe-eval), pero el proceso general es similar: definir la política (meta o cabecera), habilitar lo que la app necesita y bloquear el resto. Con Angular actualizado, es posible lograr políticas bastante seguras sin debilitar tanto las directivas gracias a las mejoras mencionadas.
Implementación de CSP en aplicaciones Next.js (React SSR)
Next.js es un framework React que soporta renderizado del lado del servidor (SSR) y generación de sitios estáticos. Dada su naturaleza híbrida, la implementación de CSP en Next.js puede abordarse en dos niveles: a nivel de aplicación (configuración en el servidor Node de Next) y a nivel de documento (meta tags en el HTML). Recomendaremos la configuración mediante cabeceras HTTP, ya que Next.js ofrece facilidades para ello, incluyendo casos de SSR.
1. Configuración global de la cabecera CSP en Next.js: La forma más directa es editar el archivo next.config.js de tu proyecto para añadir una sección de headers. Next.js permite definir encabezados personalizados para ciertas rutas. Podemos aprovechar esto para establecer Content-Security-Policy en todas las rutas. Por ejemplo, en next.config.js:
module.exports = {
async headers() {
return [
{
source: "/(.*)", // aplicar a todas las rutas
headers: [
{
key: "Content-Security-Policy",
value: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';"
}
]
}
];
}
};
En este bloque, cada petición que coincida con /(.*) (todas) incluirá la cabecera CSP con la política especificada. Hemos puesto un ejemplo de política que permite los recursos del propio sitio, incluye ‘unsafe-inline’ y ‘unsafe-eval’ en scripts por precaución (habría que evaluarlos según la app; idealmente se eliminan si no son necesarios), permite imágenes del mismo origen y data URIs, y prohíbe que la página sea incrustada en frames de otros (frame-ancestors ‘none’). Importante: ajusta las fuentes externas según tus necesidades reales (por ejemplo, si usas un CDN para scripts o estilos, inclúyelo en la lista).
Next.js aplicará esta cabecera tanto para páginas servidas mediante SSR como para páginas estáticas. Si despliegas en Vercel u otro host, esta configuración sigue funcionando ya que forma parte del código de Next.
2. Uso de _document.js (opcional): Alternativamente (o adicionalmente), Next.js permite agregar elementos en el <head> de las páginas mediante un documento personalizado (pages/_document.js o .tsx). Algunos desarrolladores optan por insertar la CSP como meta tag allí. El resultado es equivalente, pero ten en cuenta que si ya estamos enviando la cabecera HTTP mediante next.config.js, no es necesario duplicar con meta. La ventaja de usar la cabecera es que incluso si por error se carga algún HTML fuera de _document (poco probable en Next), la política seguirá aplicándose. Por ello, preferimos la configuración de cabecera global.
3. CSP con nonces en Next (CSP estricta): Next.js soporta estrategias avanzadas de CSP usando nonces para scripts, útil si quieres una política estricta sin ‘unsafe-inline’. La idea es generar un nonce aleatorio por cada solicitud y aplicarlo a cada <script> inline que Next pudiera renderear (por ejemplo, el estado de hidratación) y también incluir ese nonce en la directiva CSP (script-src ‘nonce-abcdef’). Next 13 introdujo la capacidad de hacer esto vía Middleware antes de renderizar la página.
Implementar esto requiere escribir una función middleware() que genere un nonce, adjunte la cabecera CSP con ese nonce en script-src y asegure que los scripts inline de Next lleven ese atributo. Es un tema avanzado, pero mencionarlo es útil: con nonces, puedes evitar totalmente ‘unsafe-inline’ en scripts manteniendo la funcionalidad. Next.js proporciona guías para este enfoque en su documentación oficial.
4. Considera los aspectos específicos de Next: Next.js SSR genera algún contenido inline en la página (por ejemplo, un script JSON con los datos de hidratación). Afortunadamente, Next marca ese script con type=»application/json» en lugar de JavaScript ejecutable, así que no viola CSP. Sin embargo, si tu Next app utiliza la componente <Script> para cargar scripts de terceros (analytics, chat widgets, etc.), tendrás que incluir esos dominios en script-src y quizá en frame-src/connect-src según lo que hagan. También, si tienes páginas que renderizan contenido externo (iframes, imágenes de otros dominios), ajusta las directivas correspondientes.
5. Prueba tu aplicación Next.js: Después de configurar la CSP en next.config.js, ejecuta tu aplicación en desarrollo o en un entorno de staging. Abre el sitio en un navegador con la consola dev tools abierta para capturar cualquier violación CSP en SSR o CSR. Next.js en desarrollo puede inyectar ciertos scripts de hot-reload desde localhost o webpack that podrían necesitar relajación de CSP en dev (en producción eso no ocurre).
Es posible que quieras probar directamente en build de producción. Usa el modo Report-Only si lo prefieres, configurando Content-Security-Policy-Report-Only en lugar de la cabecera normal durante pruebas, para ver informes sin bloquear. Ajusta la política según sea necesario y luego aplícala de forma definitiva.
En síntesis, Next.js facilita mucho aplicar CSP globalmente mediante su configuración de cabeceras. Con unos pocos pasos puedes proteger tu aplicación Next tanto en sus páginas SSR como en las estáticas, asegurando que el navegador solo cargue lo que tú hayas permitido.
Implementación de CSP en sitios WordPress (con o sin plugins)
WordPress, al ser un CMS popular y extensible mediante plugins, puede beneficiarse enormemente de CSP, pero su implementación requiere considerar dónde agregar la política (ya que no editamos HTML estático sino que WordPress genera páginas dinámicamente en PHP). Existen dos enfoques: usando un plugin para manejar la cabecera CSP o añadiéndola manualmente en la configuración/tema. A continuación, cubrimos ambos.
1. Implementación con plugins (método sencillo): La forma más fácil de añadir CSP en WordPress es usando un plugin de seguridad o de cabeceras HTTP. Un plugin recomendado es HTTP Headers (por Dimitar Ivanov), disponible gratuitamente en el repositorio de WordPress. Para usarlo:
- Ve al Escritorio de WordPress > Plugins > Añadir nuevo. Busca «HTTP Headers» e instala/activa el plugin.
- Una vez activado, ve a Ajustes > HTTP Headers en tu dashboard. Allí encontrarás secciones para diferentes cabeceras de seguridad.
- Localiza la opción Content-Security-Policy. Podrás introducir tu política CSP personalizada en un campo de texto.
Configura la política según las necesidades de tu sitio WordPress. Por ejemplo, podrías ingresar:
default-src 'self'; script-src 'self' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline';
- (Solo es un ejemplo; incluye todos los orígenes externos que use tu WP, p. ej. dominios de APIs, CDN de librerías JS/CSS, iframes incrustados como YouTube/Vimeo – estos últimos requieren directivas frame-src/media-src específicas.)
- Guarda los cambios. El plugin añadirá automáticamente la cabecera CSP en las respuestas de tu sitio, sin que tengas que tocar código.
Además de HTTP Headers, existen otros plugins de seguridad integrales (como Wordfence, iThemes Security, Sucuri etc.) que en versiones recientes han incorporado opciones para configurar CSP. Si ya usas alguno, revisa sus opciones de «Security Headers». En general, usar un plugin es conveniente porque centraliza la gestión de CSP y otras cabeceras desde el admin de WP, y evita editar archivos manualmente.
2. Implementación manual (en código o servidor): Si prefieres no depender de plugins, puedes agregar CSP manualmente de dos formas: a nivel de servidor web o mediante código en WordPress.
A nivel servidor (.htaccess o configuración del hosting): Similar a casos anteriores, puedes editar el archivo .htaccess en la raíz de WordPress (si usas Apache) para incluir la cabecera CSP. Inserta directivas Header set Content-Security-Policy … con la política deseada. Por ejemplo:
<IfModule mod_headers.c>
Header set Content-Security-Policy "default-src 'self'; img-src 'self' https://misimagenes.s3.amazonaws.com; script-src 'self' 'unsafe-inline' https://ajax.googleapis.com; style-src 'self' 'unsafe-inline';"
</IfModule>
- Coloca esto antes de la sección de WordPress (# BEGIN WordPress) para que no sea alterado por WP. Si tu sitio está en un hosting Nginx, puedes pedir al proveedor agregar las cabeceras CSP en la configuración, o usar un archivo de configuración personalizada si lo permiten.
En el código de WordPress (functions.php): WordPress permite enviar cabeceras personalizadas usando hooks. Puedes editar el archivo functions.php de tu tema (o mejor aún, de un tema hijo para preservar cambios) y añadir algo como:
function agregar_csp_header() {
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline'; object-src 'none';");
}
add_action('send_headers', 'agregar_csp_header');
- Esta función utiliza header() de PHP para anexar la cabecera CSP con la política indicada en cada respuesta, aprovechando el hook send_headers que WordPress ejecuta al preparar las cabeceras. Ajusta la política en la cadena según tus necesidades (por ejemplo, aquí se permitió ‘unsafe-inline’ en scripts suponiendo que quizá hay algún script inline en el theme; si no, es mejor omitirlo). Tras guardar cambios, carga tu sitio y verifica (en las herramientas de red del navegador) que la cabecera CSP esté presente.
Importante: implementar CSP via PHP es efectivo, pero ten presente que si una vulnerabilidad llegase a permitir a un atacante inyectar código antes de que las cabeceras se envíen, podría potencialmente modificar la política. Por eso, algunos expertos sugieren que, cuando sea posible, las cabeceras de seguridad se establezcan en el servidor web (Apache/Nginx) en lugar de hacerlo a nivel de la aplicación. Aun así, para la mayoría de casos prácticos, añadirlo en functions.php es seguro y funciona bien.
3. Considera los recursos utilizados por WordPress: Un sitio WordPress típico puede cargar recursos de múltiples orígenes: scripts de la librería jQuery (a veces desde Google CDN), iframes de YouTube/Vimeo, anuncios o widgets embebidos, estilos de fuentes externas (Google Fonts), etc., además de las imágenes subidas. Al escribir tu CSP, deberás contemplar todo esto. Por ejemplo:
- Si tu theme o plugins cargan scripts de terceros, inclúyelos en script-src.
- Para videos incrustados (iframe de YouTube), añade el dominio correspondiente en frame-src o child-src (y seguramente en script-src si hay scripts asociados).
- Las imágenes de tus posts que estén en tu propio dominio están cubiertas por ‘self’ en img-src. Pero si usas un CDN para imágenes, incluye su dominio.
- WordPress por defecto no pone scripts inline en el contenido, pero algunos plugins podrían hacerlo (por ejemplo, un plugin de analytics que inserte un <script>…code…</script> inline). De ser así, tendrías que permitir ‘unsafe-inline’ o mejor migrar a una solución que no use inline scripts.
4. Prueba exhaustivamente: Tras activar CSP en WordPress (sea por plugin o manual), navega por varias páginas de tu sitio (páginas, entradas con vídeos, formularios, etc.) y observa la consola del navegador. Habilita el modo Report-Only si quieres recopilar informes sin arriesgar funcionalidades inicialmente.
Es común al principio ver varios bloqueos en la consola: por ejemplo, errores indicando que alguna fuente o script de cierto dominio fue bloqueado por CSP. Toma nota de esos dominios legítimos y agrégalos a la política. WordPress puede ser dinámico y los usuarios administradores podrían agregar contenido con iframes en posts, así que considera políticas relativamente flexibles si tu caso lo requiere (por ejemplo, permitiendo iframe-src https: para no bloquear incrustaciones diversas, aunque esto reduce la seguridad). Siempre que sea posible, sé específico en los orígenes permitidos en lugar de usar comodines amplios.
Implementar CSP en WordPress puede parecer laborioso debido a la diversidad de componentes, pero las recompensas en seguridad son grandes: protegerás tu sitio y a tus visitantes de cargas inesperadas de contenido malicioso. Y recuerda que una vez implementada, mantén la política actualizada cuando instales nuevos plugins o añadas funcionalidades que requieran recursos externos, ajustando las directivas en consecuencia.
Uso del modo «Report-Only» y mejores prácticas para una CSP segura
Implementar CSP con éxito es un proceso iterativo. Un paso fundamental en ese proceso es utilizar el modo Report-Only antes de forzar una política, así como seguir ciertas mejores prácticas para equilibrar seguridad y funcionalidad.
Content-Security-Policy-Report-Only: probando tu CSP sin riesgos
El modo Report-Only consiste en enviar tu política CSP bajo la cabecera Content-Security-Policy-Report-Only en lugar de la cabecera normal. Cuando el navegador ve una política en modo reporte, no bloqueará los recursos que la violen, pero sí registrará internamente las violaciones. Esto te permite ver qué rompería tu CSP si estuviera activa, sin afectar a los usuarios.
Para usarlo, configura en tu servidor (o meta tag, si es el caso*) la cabecera de esta forma:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://maps.googleapis.com; report-uri /csp-violation-report-endpoint;
En este ejemplo, la política es igual a la que querríamos imponer (permitir scripts propios y de Google Maps, bloquear el resto). Hemos añadido una directiva report-uri, que es opcional pero útil: le indica al navegador dónde enviar un informe HTTP cuando ocurra una violación CSP.
En el ejemplo, sería un endpoint /csp-violation-report-endpoint en nuestro servidor, el cual podríamos preparar para recibir JSON con detalles de la violación. Si no especificas report-uri o la más moderna report-to, igualmente puedes inspeccionar las violaciones en la consola del navegador (pestaña Console, suelen aparecer como mensajes CSP).
Caveat: El modo Report-Only como tal solo existe vía cabecera HTTP, no vía meta tag (no hay http-equiv equivalente). Por ello, para pruebas conviene habilitar la cabecera temporalmente. Muchos frameworks/servidores te permiten habilitar fácilmente Report-Only; por ejemplo, si usas Helmet en Node, puedes pasarle la opción reportOnly: true en la config de CSP, o simplemente configurar la cabecera con el nombre report-only.
Mientras tu sitio esté en modo reporte, navega por todas las secciones y funcionalidades críticas. Revisa los reportes generados o la consola para ver qué estaría siendo bloqueado: cada entrada te indicará la directiva infringida y la URL del recurso en cuestión. Esta es la lista de cosas a considerar para ajustar:
- Recursos legítimos bloqueados: Si ves que, por ejemplo, se habría bloqueado un script de cdnjs.cloudflare.com que tu sitio usa, entonces debes añadir ese origen a script-src en tu política CSP. Continúa así con cada elemento legítimo hasta que no queden false positives.
- Recursos inesperados: A veces el ejercicio de Report-Only revela cargas que no sabías que ocurrían. Por ejemplo, un plugin podría estar llamando a un dominio externo. Esto es útil porque destapa dependencias ocultas. Deberás decidir si permites ese origen en tu CSP (si confías en él) o si eliminas/reemplazas ese componente de tu sitio.
- Inline scripts o eval no permitidos: Si tu CSP no permitía inline scripts o eval y el reporte indica violaciones de ese tipo, tendrás que decidir cómo resolverlo. Idealmente, elimina la necesidad de código inline en tu aplicación. Si no es posible, podrías optar por usar nonces/hashes para autorizar esos fragmentos (más seguro) o, en última instancia, relajar la política con ‘unsafe-inline’ o ‘unsafe-eval’ temporalmente. Recuerda que habilitar ‘unsafe-inline’ en scripts reduce significativamente la protección, por lo que es preferible buscar alternativas antes.
Una vez que los reportes ya no muestran violaciones legítimas, estás listo para cambiar a la cabecera Content-Security-Policy normal y hacer que el navegador empiece a bloquear efectivamente todo lo no permitido. Sigue monitorizando después del despliegue; quizás surja algo no contemplado (por ejemplo, un usuario incrusta contenido nuevo, o un CDN cambia de dominio). Mantén la alerta especialmente las primeras semanas.
Mejores prácticas para una CSP sólida (sin romper tu sitio)
Ya con la experiencia de implementar CSP en varios entornos, resumimos algunas recomendaciones generales:
- Comienza restrictivo, luego abre excepciones necesarias: Es tentador escribir una CSP muy laxa «para no romper nada» (ej. default-src * y listo), pero eso prácticamente anula su propósito. En vez de eso, empieza con ‘self’ y unas pocas fuentes esenciales, y ve ampliando solo cuando algo legítimo lo requiera.
Cada dominio externo añadido debe ser considerado: ¿confías en ese host? ¿Es preciso permitirlo en todas las páginas o solo en algunas secciones (en cuyo caso, quizá CSP dinámica o por página sería necesaria)? - Evita comodines y fuentes inseguras: No uses * salvo quizá en casos muy específicos (por ejemplo, img-src * data: si quieres permitir imágenes de cualquier origen; pero incluso ahí, evaluar riesgos). Nunca uses * en script-src o style-src para «quitarte problemas» – estarías dejando la puerta abierta a XSS de cualquier origen.
Del mismo modo, trata de no usar ‘unsafe-inline’ ni ‘unsafe-eval’ a menos que no haya alternativa viable. Si debes usarlos (como vimos en Angular/WordPress), comprende que estás debilitando la política y intenta limitarlo: por ejemplo, ‘unsafe-inline’ solo en estilo, pero no en script. O quizás ‘unsafe-eval’ solo mientras migras a una versión que no lo requiera. - Usa nonces o hashes para código inline: Si tu aplicación necesita ejecutar algún fragmento inline (por ejemplo, un script de configuración incluido en el HTML), considera usar nonces o hashes en lugar de habilitar todo inline.
Un nonce es una cadena aleatoria que generas por solicitud y se inserta tanto en la CSP (p.ej. script-src ‘self’ ‘nonce-ABC123’) como en el atributo de tu <script> inline (<script nonce=»ABC123″>…). El navegador permitirá ese script porque el nonce coincide. Los hashes funcionan calculando un SHA256 del contenido del script y colocándolo en la CSP (‘sha256-XYZ…=’). Estas técnicas permiten autorizar casos puntuales de inline sin abrir la puerta globalmente. Frameworks como Next.js y Angular ya facilitan el uso de nonces/hashes, úsalos si están disponibles. - Ten en cuenta otras directivas útiles: CSP tiene muchas directivas; algunas no afectan la carga de recursos sino comportamientos. Por ejemplo, upgrade-insecure-requests es muy útil si migras de HTTP a HTTPS, forzando al navegador a convertir peticiones http en https. base-uri ‘none’ evita que atacantes cambien la base URI de tu sitio. form-action ‘self’ asegura que formularios solo se envíen a tu servidor.
Revisa las directivas recomendadas para tu caso de uso; una CSP completa suele incluir: default-src, script-src, style-src, img-src, font-src, connect-src, frame-ancestors (o child-src), object-src, base-uri y opcionalmente form-action. Si dejas alguna sin especificar, heredará de default-src. - Mantén la CSP actualizada: Tu sitio web no es estático en el tiempo. Es posible que en el futuro agregues un servicio de chat, un nuevo proveedor de APIs, o cambies recursos a otro CDN. Cada cambio en recursos externos debe reflejarse en tu CSP.
Documenta en tu código/configuración qué políticas tienes y por qué, de modo que si otro desarrollador toma el proyecto, entienda cómo ajustar la CSP al hacer cambios. Asimismo, de vez en cuando revisa tu CSP para ver si permite más de lo necesario (por ejemplo, si quitaste un widget de Twitter, podrías ya no necesitar platform.twitter.com en la política). - Apóyate en herramientas: Existen herramientas online como Report URI (report-uri.com) para recolectar reportes CSP de navegadores en producción de forma conveniente (requiere añadir su endpoint en report-uri). También herramientas como CSP Evaluator de Google que analizan tu política en busca de debilidades conocidas. Úsalas para pulir detalles. Adicionalmente, sitios como securityheaders.io pueden escanear tu sitio y verificar si la cabecera CSP está presente y valorarla respecto a buenas prácticas.
En conclusión, Content Security Policy es una potente aliada para proteger tu web de carga de contenido malicioso, especialmente contra XSS. Su correcta implementación requiere un análisis cuidadoso de qué necesita tu aplicación y una fase de prueba con Report-Only para no afectar la experiencia de usuario inadvertidamente.
Siguiendo los pasos y recomendaciones descritos, podrás implementar CSP de forma segura y fortalecer significativamente la postura de seguridad de tu sitio sin romper su funcionalidad legítima. Recuerda: una CSP bien afinada significa que tu sitio web solo ejecuta lo que tú has previsto que ejecute – nada más, y eso es un gran salto adelante en seguridad en el mundo web actual.