Configurar Content Security Policy no consiste en copiar una cabecera enorme y esperar que todo siga funcionando. Una política demasiado permisiva apenas protege; una demasiado estricta, desplegada sin pruebas, puede bloquear scripts, estilos, imágenes, formularios o conexiones que la web necesita.
La estrategia más segura es progresiva: identificar los recursos legítimos, observar primero las infracciones con Content-Security-Policy-Report-Only, corregir dependencias y aplicar después una política explícita. CSP pasa así de ser una lista improvisada de dominios a convertirse en una descripción verificable de lo que la página puede cargar y ejecutar.
CSP es una capa adicional de seguridad. Reduce el impacto de determinadas inyecciones y cargas no autorizadas, pero no corrige por sí sola una vulnerabilidad XSS ni sustituye la validación de entradas, la codificación de salidas, las actualizaciones o el mantenimiento de la aplicación.
Qué es Content Security Policy y qué controla
Content Security Policy, o CSP, es un mecanismo de seguridad que permite indicar al navegador qué orígenes y tipos de recursos están autorizados en una página. La política se entrega normalmente mediante la cabecera HTTP Content-Security-Policy y se divide en directivas separadas por punto y coma.
La guía de Content Security Policy de MDN explica su uso para limitar los recursos que una página puede cargar y para mitigar amenazas como el cross-site scripting. La referencia de la cabecera Content-Security-Policy reúne las directivas disponibles y su sintaxis.
Una política inicial muy restrictiva podría escribirse así:
Content-Security-Policy: default-src 'self'
default-src 'self' establece como regla general que los recursos solo pueden proceder del mismo origen que el documento. El origen no equivale únicamente al dominio: también intervienen el esquema y el puerto. Por eso, un recurso alojado en otro subdominio o en un proveedor externo puede quedar bloqueado aunque pertenezca al mismo proyecto.
Las directivas más específicas sustituyen el valor general para su categoría. Si existe img-src, las imágenes se evalúan contra esa directiva; si no existe, el navegador puede recurrir a default-src como alternativa.
Qué ataques ayuda a mitigar CSP y cuáles quedan fuera
El caso de uso más conocido es reducir el impacto de un ataque XSS. En un XSS, datos que deberían tratarse como contenido terminan interpretándose como código en el navegador. Una CSP bien diseñada puede impedir la ejecución de scripts insertados en línea o cargados desde un origen no autorizado.
También puede limitar otros comportamientos peligrosos:
- Carga de scripts de terceros no autorizados:
script-srcrestringe desde dónde se puede cargar JavaScript. - Conexiones salientes inesperadas:
connect-srclimita destinos utilizados por mecanismos comofetch, XMLHttpRequest o WebSocket. - Inserción de la página en marcos:
frame-ancestorscontrola qué sitios pueden embeberla mediante marcos compatibles. - Envío de formularios a destinos inesperados:
form-actionrestringe las direcciones a las que pueden enviarse formularios. - Carga de imágenes, fuentes, estilos o marcos desde orígenes no previstos: cada familia puede limitarse con su directiva correspondiente.
- Carga de contenido HTTP desde una página HTTPS: una política puede ayudar a evitar recursos inseguros o solicitar su actualización cuando sea viable.
Sin embargo, CSP no demuestra que una web sea segura. No elimina el código vulnerable, no protege una cuenta con una contraseña débil, no actualiza plugins y tampoco evita que un origen expresamente autorizado entregue contenido comprometido. Conviene combinarla con un proceso de mantenimiento del CMS, actualizaciones, permisos mínimos y revisión del código.
Directivas esenciales para construir la política
No todas las webs necesitan las mismas directivas. Una página estática, una tienda, un WordPress con plugins y una aplicación de una sola página cargan recursos diferentes. La política debe reflejar esas necesidades reales.
| Directiva | Qué controla | Punto de partida habitual |
|---|---|---|
default-src | Regla de respaldo para categorías sin una directiva más específica | 'self' o 'none', según el diseño |
script-src | Orígenes y mecanismos autorizados para ejecutar scripts | Origen propio y nonces o hashes cuando sean necesarios |
style-src | Hojas de estilo y estilos autorizados | Origen propio y una estrategia específica para estilos en línea |
img-src | Imágenes | Origen propio y proveedores de imágenes utilizados |
font-src | Fuentes web | Origen propio y proveedor de fuentes, si existe |
connect-src | Conexiones iniciadas por scripts | API propia y servicios externos imprescindibles |
frame-src | Contenido que la página puede cargar dentro de marcos | Proveedores concretos de vídeo, mapas u otros widgets |
frame-ancestors | Quién puede insertar la página dentro de un marco | 'none' si no debe embeberse |
form-action | Destinos permitidos para formularios | 'self' y procesadores externos imprescindibles |
object-src | Recursos cargados mediante elementos de objeto | 'none' cuando no se utilicen |
base-uri | Valores autorizados para el elemento base | 'self' o 'none' |
Una base razonable para una web que sirve todos sus archivos desde el mismo origen podría ser:
Content-Security-Policy:
default-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
form-action 'self';
No debe copiarse directamente en producción. Antes hay que comprobar si existen fuentes externas, imágenes de una CDN, vídeos incrustados, analítica, formularios de terceros o peticiones a una API. Para entender dónde interviene la cabecera dentro de la comunicación entre navegador y servidor, puede consultarse la guía sobre cómo funcionan una URL y el protocolo HTTP.
Cabecera HTTP o metaetiqueta: cuál conviene utilizar
La forma preferente de entregar CSP es una cabecera HTTP incluida en la respuesta del documento:
Content-Security-Policy: default-src 'self'; object-src 'none'
La cabecera se configura en el servidor web, la plataforma de alojamiento, el proxy inverso, una función perimetral o la propia aplicación. Ofrece un control centralizado y permite utilizar el mecanismo de solo informe durante el despliegue.
Cuando no es posible modificar las cabeceras, puede utilizarse una metaetiqueta dentro del head:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; object-src 'none'">
La metaetiqueta tiene limitaciones importantes. Solo afecta al contenido procesado después de que el navegador la encuentre, por lo que debe aparecer lo antes posible. Además, no sustituye todas las capacidades disponibles mediante cabeceras y no permite desplegar Content-Security-Policy-Report-Only.
| Criterio | Cabecera HTTP | Metaetiqueta |
|---|---|---|
| Aplicación | Desde la respuesta HTTP | Desde el HTML |
| Report-Only | Sí | No |
| Control centralizado | Sí | Depende de cada documento |
| Momento de aplicación | Al procesar la respuesta | Después de encontrar la etiqueta |
| Uso recomendado | Opción principal | Alternativa limitada si no se controlan las cabeceras |
Cómo configurar Content Security Policy en una web sin romperla
1. Crear un inventario de recursos
Antes de escribir la política, conviene recorrer las páginas representativas y anotar qué carga cada una. El inventario debería incluir:
- scripts propios y de terceros;
- hojas de estilo y estilos insertados en el HTML;
- imágenes locales, CDN y direcciones
data:si aparecen; - fuentes externas;
- vídeos, mapas, pagos y otros marcos;
- APIs, WebSockets y servicios de analítica;
- destinos de formularios;
- código generado por el tema, el CMS o sus extensiones.
La pestaña Red de las herramientas del navegador permite observar las solicitudes realizadas. La guía de navegadores y herramientas DevTools sirve como introducción a estas funciones.
2. Empezar con Content-Security-Policy-Report-Only
El modo Report-Only permite evaluar una política sin aplicar sus bloqueos. El navegador continúa cargando los recursos, pero muestra las infracciones que habría producido la política.
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self';
font-src 'self';
connect-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
form-action 'self';
Este ejemplo es deliberadamente estricto para descubrir dependencias. Si una web utiliza recursos externos legítimos, aparecerán avisos. No todos deben resolverse ampliando la lista: primero hay que determinar si el recurso es necesario, si puede alojarse localmente o si procede de una extensión que debería retirarse.
Report-Only no protege frente a las acciones detectadas porque no bloquea los recursos. Es una fase de observación, no el estado final de seguridad.
3. Interpretar los avisos de la consola
En DevTools, la consola suele indicar tres elementos útiles: la directiva que se ha incumplido, el recurso afectado y la política utilizada. El diagnóstico debe seguir este orden:
- Identificar el tipo de recurso bloqueado: script, estilo, imagen, fuente, conexión o marco.
- Localizar el dominio, la URL o el bloque en línea que provoca el aviso.
- Comprobar si ese recurso es legítimo y necesario.
- Modificar la directiva específica, no
default-srcde forma indiscriminada. - Recargar y repetir la prueba en las páginas y flujos afectados.
Por ejemplo, un aviso relacionado con connect-src no se corrige añadiendo el dominio a script-src. Si el problema es una llamada a una API, debe revisarse connect-src. Mantener esta correspondencia evita políticas difíciles de entender.
4. Autorizar solo los orígenes necesarios
Supongamos que una web utiliza scripts propios, imágenes locales y de imagenes.ejemplo.net, fuentes desde fuentes.ejemplo.net y una API en api.ejemplo.net. Una política orientativa sería:
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' https://imagenes.ejemplo.net;
font-src 'self' https://fuentes.ejemplo.net;
connect-src 'self' https://api.ejemplo.net;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
form-action 'self';
Los dominios son ilustrativos. Deben sustituirse por los observados y verificados en la web real. Evitar comodines amplios facilita saber qué proveedor está autorizado y reduce la superficie permitida.
5. Probar todos los flujos importantes
Visitar la portada no basta. Hay que comprobar, como mínimo, inicio y cierre de sesión, formularios, buscador, comentarios si existen, consentimiento de cookies, compra o reserva, reproductores, mapas, panel de administración y cualquier función que cargue contenido después de la primera renderización.
También conviene probar con distintos perfiles y tamaños de pantalla. Algunos scripts solo se cargan al aceptar una categoría de cookies, abrir un menú o completar un formulario.
6. Cambiar de Report-Only a aplicación efectiva
Cuando los avisos legítimos estén resueltos, se cambia el nombre de la cabecera:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' https://imagenes.ejemplo.net;
font-src 'self' https://fuentes.ejemplo.net;
connect-src 'self' https://api.ejemplo.net;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
form-action 'self';
Después debe repetirse el recorrido. Ahora un incumplimiento sí puede bloquear una función. El cambio debería desplegarse con un procedimiento de reversión y revisarse cada vez que se añadan plugins, proveedores o recursos externos. La gestión de servidores web aporta contexto sobre la administración y comprobación de servicios HTTP.
Scripts en línea: nonce, hash y el riesgo de unsafe-inline
Los scripts escritos directamente en el HTML presentan una dificultad: una política basada solo en 'self' no los autoriza por el simple hecho de que el documento sea propio. Existen tres enfoques habituales.
Nonce generado para cada respuesta
Un nonce es un valor impredecible generado para una respuesta concreta. Se incluye tanto en la política como en el elemento autorizado:
Content-Security-Policy: script-src 'self' 'nonce-VALOR_ALEATORIO'
<script nonce="VALOR_ALEATORIO">
iniciarAplicacion();
</script>
El servidor debe generar un valor nuevo y difícil de predecir para cada respuesta, además de colocar exactamente el mismo valor en la cabecera y en los scripts legítimos. Un nonce fijo reutilizado pierde el objetivo de distinguir el código autorizado en cada respuesta.
Hash del contenido autorizado
Otra opción consiste en calcular el hash del bloque exacto y añadirlo a script-src. Es útil para fragmentos estáticos que no cambian con frecuencia. Si se modifica cualquier carácter relevante del bloque, el hash deja de coincidir y el navegador lo bloquea hasta actualizar la política.
Nonce y hash no son intercambiables en todos los escenarios: el nonce encaja mejor con respuestas generadas dinámicamente; el hash puede resultar cómodo para contenido estable.
Por qué evitar unsafe-inline
Añadir 'unsafe-inline' permite ejecutar código en línea de forma amplia y reduce una de las barreras que CSP pretende establecer frente a la inyección. Puede parecer la solución rápida cuando un tema o plugin genera numerosos bloques en línea, pero debería tratarse como una concesión temporal que requiere revisión.
La alternativa preferible es mover el código a archivos propios, adaptar la aplicación para usar nonces o hashes y eliminar fragmentos innecesarios. El mismo principio se aplica a los estilos, aunque su migración puede requerir revisar atributos style y bloques generados por componentes.
Cómo verificar que la política se está enviando
La comprobación debe hacerse sobre la respuesta HTTP real, no solo sobre el archivo de configuración del servidor:
- Abrir DevTools y entrar en la pestaña Red.
- Recargar la página.
- Seleccionar la solicitud correspondiente al documento HTML principal.
- Revisar las cabeceras de respuesta.
- Confirmar si aparece
Content-Security-Policy-Report-OnlyoContent-Security-Policy. - Comprobar la consola y ejecutar los flujos funcionales.
Ver la cabecera acredita que esa respuesta contiene la política observada. No demuestra por sí solo que todas las páginas, códigos de estado, redirecciones o respuestas generadas por cachés estén configuradas igual. Deben inspeccionarse muestras representativas.
También hay que buscar políticas duplicadas. Una cabecera añadida por el servidor, otra por un proxy y una metaetiqueta pueden producir un resultado más restrictivo del previsto. En lugar de asumir que una de ellas sobrescribe a las demás, conviene identificar quién genera cada política y consolidar la configuración.
Errores habituales al desplegar CSP
- Activar una política estricta directamente en producción: puede dejar sin funcionar elementos esenciales antes de conocer las dependencias.
- Autorizar un dominio cada vez que aparece un aviso: primero hay que confirmar que el recurso sea necesario y legítimo.
- Usar comodines amplios: simplifican el despliegue, pero reducen el control sobre los orígenes autorizados.
- Añadir unsafe-inline como solución permanente: evita bloqueos a costa de debilitar la protección frente al código insertado en línea.
- Olvidar las conexiones de JavaScript: una API puede fallar aunque sus scripts se carguen correctamente si falta en
connect-src. - No probar páginas secundarias: formularios, paneles, pagos y contenido multimedia suelen tener dependencias distintas de la portada.
- Confundir Report-Only con protección activa: el modo de informe detecta infracciones, pero no las bloquea.
- Dar por terminada la política: cualquier cambio de tema, plugin, proveedor o arquitectura puede exigir una revisión.
Checklist final de despliegue
- Se ha creado un inventario de scripts, estilos, imágenes, fuentes, marcos, formularios y conexiones.
- La política inicial se ha probado mediante
Content-Security-Policy-Report-Only. - Cada aviso se ha asociado con su directiva y recurso concreto.
- Solo se han autorizado orígenes necesarios y verificados.
- Los scripts en línea se han eliminado o protegido mediante una estrategia de nonce o hash.
- Se ha evitado
'unsafe-inline'salvo como excepción documentada y temporal. - Se han probado formularios, autenticación, administración, multimedia y servicios externos.
- La cabecera efectiva aparece en la respuesta HTTP del documento.
- Se ha repetido la prueba después de pasar de Report-Only a bloqueo.
- Existe un procedimiento para revisar CSP cuando cambie la web.
La clave para configurar Content Security Policy en una web es avanzar desde la observación hacia la restricción. Una política corta, entendida y ajustada a los recursos reales suele resultar más útil que una lista extensa copiada de otro proyecto. Report-Only permite descubrir dependencias; DevTools ayuda a localizar cada incumplimiento; y las directivas específicas permiten autorizar únicamente lo que la aplicación necesita.
Preguntas frecuentes sobre Content Security Policy
¿CSP evita por completo los ataques XSS?
No. Puede reducir su impacto al limitar la ejecución de scripts y la carga de recursos, pero no elimina la vulnerabilidad que permitió la inyección. Debe acompañarse de validación, codificación de salida, actualizaciones y otras medidas de desarrollo seguro.
¿Puedo copiar la política CSP de otra web?
No es recomendable. Cada web utiliza recursos, proveedores y flujos diferentes. Una política ajena puede bloquear funciones necesarias o autorizar dominios que tu sitio no utiliza.
¿Report-Only bloquea contenido?
No. Content-Security-Policy-Report-Only permite observar qué incumplimientos se producirían, pero los recursos continúan cargándose. Para aplicar el bloqueo debe utilizarse Content-Security-Policy.
¿Es mejor configurar CSP como cabecera o como metaetiqueta?
La cabecera HTTP es la opción preferente porque se aplica desde la respuesta, permite Report-Only y ofrece mayor control. La metaetiqueta es una alternativa limitada cuando no se pueden modificar las cabeceras.
¿Por qué se rompe JavaScript al activar CSP?
Normalmente porque el script procede de un origen no autorizado, está escrito en línea o realiza conexiones no contempladas en connect-src. La consola del navegador indica la directiva que ha provocado el bloqueo.
¿Hay que revisar CSP después de instalar un plugin?
Sí. Un plugin, tema o servicio externo puede añadir nuevos scripts, estilos, marcos o conexiones. El cambio debe probarse primero y la política solo debe ampliarse si el nuevo recurso es necesario y fiable.


