Content Security Policy para chatbots web: cómo autorizar de forma segura el widget, la API, las imágenes y el streaming
Una CSP práctica para chatbots web solo permite los scripts, conexiones API, streams e imágenes estrictamente necesarios, sin recurrir a comodines innecesarios.

En el navegador, un chatbot web rara vez consiste en un solo archivo JavaScript. Un cargador abre el widget, una API recibe los mensajes, las respuestas llegan en forma de stream y las imágenes de perfil o los archivos multimedia pueden estar alojados en un dominio diferente. Una Content Security Policy (CSP) visibiliza estas rutas y las limita: el navegador solo carga o se conecta a lo que el sitio web permite expresamente.
Se trata de una segunda capa de protección esencial contra ataques de Cross-Site Scripting y contenido de terceros no deseado. Sin embargo, una CSP no arregla una API insegura ni soluciona la falta de autenticación, una validación de entradas deficiente o una prompt injection. Su función es reducir las posibilidades de ejecución de código inyectado y limitar el alcance de un fallo. Por ello, lo primordial es contar con una directiva lo más reducida y probada posible en lugar de una lista extensa de dominios autorizados de forma generalizada.
Por qué los widgets de chatbot requieren reglas de CSP específicas
En una página de contenido tradicional, suele bastar con recursos del propio origen. En cambio, un chatbot sigue comunicándose tras la carga inicial. Entre otros, connect-src controla fetch(), XMLHttpRequest, EventSource, WebSocket y sendBeacon(). Precisamente por aquí transitan los mensajes, las respuestas en streaming, los eventos de feedback y, si aplica, la telemetría. Si falta el origen correcto, el widget se mostrará, pero no podrá responder.
Otros elementos dependen de directivas propias. script-src determina el cargador del widget, img-src rige los avatares y las imágenes de respuesta, style-src las hojas de estilo y font-src las fuentes externas. Un widget basado en iframe requiere además frame-src. default-src actúa como alternativa para muchos tipos de recursos no especificados expresamente, pero no reemplaza un inventario consciente.
Por ello, el trabajo previo más importante no se hace en el generador de CSP, sino en el navegador: abra una página representativa, inicie una conversación, deje que se transmita una respuesta larga en streaming, abra las fuentes, envíe feedback y pruebe los casos de error y de derivación a agentes (handoff). En el panel de red de las herramientas para desarrolladores observará los orígenes a los que realmente se llama. Documente el propósito, el tipo de recurso y la persona responsable de cada host.
Autorizar por separado las cuatro rutas de datos relevantes
1. Script del widget e inicialización
Obtenga el cargador, a ser posible, de una dirección estable y versionada. Una autorización genérica como script-src https: sería demasiado amplia, ya que permitiría ejecutar scripts de cualquier dominio HTTPS. En su lugar, autorice el origen exacto de la CDN o sirva el cargador usted mismo. Si la integración requiere código inline, utilice un nonce recién generado por cada respuesta HTTP o un hash adecuado. 'unsafe-inline' no debe convertirse en una solución permanente rápida.
Un nonce solo debe asignarse a los scripts que genera la propia plantilla en el servidor. Un middleware que añada a ciegas el mismo nonce a cualquier etiqueta script existente también otorgaría confianza a etiquetas inyectadas. Para un script de terceros estático y versionado, la Subresource Integrity puede aportar una capa extra de seguridad; sin embargo, si los archivos cambian con frecuencia, el hash debe actualizarse de forma controlada.
2. API, Server-Sent Events y WebSocket
Las peticiones POST normales y una respuesta transmitida en streaming a través de fetch() necesitan el origen HTTPS de la API en connect-src. Los Server-Sent Events a través de EventSource también se incluyen ahí. Para un WebSocket, incluya expresamente el origen wss:// concreto. MDN señala que 'self' no abarca automáticamente los esquemas WebSocket en todos los navegadores. No existe una directiva propia llamada stream-src.
CSP y CORS cumplen funciones distintas. CSP determina a dónde puede conectarse la página; CORS especifica en el servidor qué orígenes tienen permiso para leer una respuesta en el navegador. Por tanto, una autorización en la CSP no resuelve ni un error de CORS ni un token de acceso caducado. Un proxy Same-Origin puede simplificar la directiva, pero debe seguir gestionando correctamente la autenticación, los límites de velocidad (rate limits), los tiempos de espera y la transmisión de errores.
3. Imágenes, avatares y archivos multimedia generados
En img-src, permita únicamente el origen propio y el origen multimedia que realmente se utilice. data: solo es necesario si el widget emplea pequeñas imágenes embebidas; blob: únicamente si el navegador genera las imágenes como URL de tipo Blob. Cada fuente adicional aumenta la superficie de ataque. Si una imagen se carga primero mediante fetch() y luego se convierte en una URL Blob, tanto connect-src como img-src pueden verse afectados.
No pruebe únicamente el avatar predeterminado. Compruebe las imágenes de vista previa, las capturas de pantalla de las fuentes, los archivos adjuntos, el modo oscuro y la representación de errores para medios no disponibles. Los parámetros de la URL pueden transferir información confidencial a los informes de CSP; por este motivo, los puntos de acceso de informes (reporting endpoints) deben procesar los informes minimizando los datos y evitar conservarlos de forma indefinida.
4. iframe, estilos, fuentes y Workers opcionales
Un widget integrado directamente en el DOM no suele necesitar un frame externo. En ese caso, frame-src 'none' puede mantenerse. Si, por el contrario, el chat se ejecuta en un iframe, autorice exclusivamente su origen exacto. Esto debe distinguirse de frame-ancestors: esta directiva define en el recurso servido qué páginas tienen permitido incrustarlo. El proveedor del widget debe, por lo tanto, configurarla adecuadamente en la respuesta de su iframe.
Para los estilos y las fuentes se aplica el mismo principio. Autorice hosts concretos y evite 'unsafe-inline' en la medida en que la integración lo permita. Los Workers o las funciones de audio solo deben añadirse si el producto realmente los utiliza. Una autorización preventiva de blob:, dominios completos con comodines o fuentes multimedia arbitrarias dificulta las auditorías posteriores.
Un ejemplo realista de CSP para un widget de chatbot
Los siguientes dominios son ejemplos reservados a propósito. Reemplácelos por los orígenes obtenidos en su propio análisis de red. El ejemplo presupone un cargador externo, una API HTTPS, un WebSocket separado para streaming y un host multimedia. No utiliza comodines genéricos:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.chat.example 'nonce-{RANDOM}';
connect-src 'self' https://api.chat.example wss://stream.chat.example;
img-src 'self' data: https://media.chat.example;
style-src 'self' 'nonce-{RANDOM}';
font-src 'self';
frame-src 'none';
worker-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'self';
form-action 'self';
upgrade-insecure-requests;{RANDOM} representa un valor criptográfico seguro generado de nuevo por cada respuesta, que debe ser idéntico en la cabecera HTTP y en los elementos script o style permitidos. Si su widget utiliza un iframe, reemplace frame-src 'none' por el origen exacto del widget. Si utiliza exclusivamente streaming HTTPS mediante fetch() o EventSource, se puede omitir el origen WebSocket. Elimine cualquier fuente que no sea necesaria tras realizar una prueba de funcionamiento completa.
Esta directiva es un punto de partida práctico, no una plantilla universal. Una CSP estricta y moderna puede controlar los scripts aún más mediante nonces o hashes y 'strict-dynamic'. Que esto sea posible sin problemas de compatibilidad depende de cómo el cargador genere scripts adicionales. Aclare este flujo con el proveedor y pruebe diferentes navegadores, modos de consentimiento (consent mode) y variantes de despliegue.
De Report-Only a la directiva aplicada
No active una nueva directiva directamente en producción sin probarla antes. El mecanismo del W3C Content-Security-Policy-Report-Only notifica las infracciones sin bloquear los recursos. De este modo se detectan hosts de imágenes olvidados, un origen de streaming diferente o código inline antes de que afecten a los usuarios. OWASP recomienda la cabecera HTTP como la vía de entrega preferida; a diferencia del elemento meta, admite la totalidad de las funciones.
- Crear un inventario: Pruebe el inicio del widget, el primer mensaje, una respuesta larga en streaming, las fuentes, las imágenes, el feedback, la derivación a agentes (handoff) y los cambios de consentimiento en múltiples tipos de páginas.
- Desplegar en modo Report-Only: Comience con la directiva estricta planificada y recopile las infracciones durante un periodo determinado. Filtre las extensiones de navegador y otras interferencias no reproducibles.
- Justificar cada host: Amplíe la directiva únicamente si una función concreta del producto requiere ese origen. Evite el uso de comodines como respuesta a alertas aisladas.
- Realizar pruebas automatizadas: Añada pruebas end-to-end que envíen un mensaje, esperen a la respuesta en streaming y carguen una imagen. Al mismo tiempo, compruebe si aparecen infracciones de CSP en la consola del navegador.
- Aplicar y monitorizar: Active la cabecera
Content-Security-Policy, mantenga en paralelo una variante Report-Only aún más estricta para observación y compare las tasas de error.
Un despliegue escalonado encaja perfectamente con un chatbot en Shadow Mode. Para métricas específicas sobre streaming, consulte el artículo sobre presupuestos de latencia y timeouts. Las infracciones de CSP deben tratarse como una señal independiente: un timeout y una conexión bloqueada requieren análisis de causa raíz totalmente distintos.
Configuraciones erróneas habituales
- Listas de fuentes demasiado amplias: El uso de
*,https:o grandes dominios con comodines hace que la directiva sea cómoda, pero débil y difícil de auditar. - Solo se prueba la carga inicial visible: El widget se abre correctamente, pero el streaming, el feedback, las imágenes o el handoff fallan más adelante.
'unsafe-inline'se mantiene de forma permanente: Una solución temporal para la compatibilidad no se llega a reemplazar por nonces, hashes o código externo.- Se confunde la CSP con el control de acceso: La directiva no sustituye los permisos en el servidor, la verificación de sesiones ni la protección contra llamadas malintencionadas a herramientas (tool calls).
- Los informes contienen demasiados datos: URL completas, parámetros de consulta o el contexto del usuario acaban almacenados innecesariamente durante mucho tiempo en los sistemas de monitorización.
- Desajustes entre Staging y Producción: Las diferencias en los hosts de CDN, API o WebSocket solo se hacen evidentes tras la puesta en producción.
Incluso un script-src estricto no convierte automáticamente a un proveedor tercero en seguro: su JavaScript se ejecuta con los privilegios que su propia página le concede. Por lo tanto, evalúe los cambios de proveedor, los nuevos subdominios y las actualizaciones del cargador como cualquier otra dependencia crítica de seguridad. El artículo sobre protección contra Prompt Injection complementa este límite del navegador con reglas para RAG, herramientas y datos.
Lista de comprobación antes del lanzamiento
- ¿Están documentados y justificados técnicamente todos los orígenes requeridos a partir de sesiones reales de navegador?
- ¿Permite
script-srcúnicamente el cargador y los scripts controlados, sin recurrir a un'unsafe-inline'genérico? - ¿Contiene
connect-srclos orígenes HTTPS, EventSource y, si aplica, WSS exactos? - ¿Están las fuentes de imágenes, estilos, fuentes tipográficas, frames y Workers separadas y definidas de la forma más estricta posible?
- ¿Se generan los nonces de nuevo para cada respuesta y se aplican únicamente a elementos de confianza?
- ¿Se han probado los cambios de consentimiento, los streams largos, las imágenes, los errores, el handoff y el comportamiento tanto en escritorio como en dispositivos móviles?
- ¿Se ha monitorizado primero la directiva en modo Report-Only antes de forzarla mediante la cabecera HTTP?
- ¿Se procesan los informes de CSP sin incluir datos personales ni parámetros de URL confidenciales innecesarios?
- ¿Existe una prueba de regresión automatizada tras realizar actualizaciones en el widget o en la infraestructura?
Conclusión
Una buena CSP para chatbots web no es una colección de excepciones, sino un mapa técnico de las rutas autorizadas en el navegador. Separe el cargador, la API, el streaming, las imágenes y los recursos de iframe, autorice los orígenes exactos e implemente la directiva primero en modo Report-Only. De este modo, el widget mantendrá su operatividad plena al tiempo que se reduce considerablemente el margen de maniobra para scripts y conexiones no deseados.
Fuentes
Convierta las visitas en mejores conversaciones
Lance un chatbot de IA útil desde el primer día
Entrene ChatReact con su sitio web, documentos y hechos aprobados para que los visitantes obtengan respuestas más rápidas y su equipo reciba menos consultas repetitivas.
Artículos relacionados
Seguir leyendo
Cómo agregar un chatbot de IA a un sitio web sin perjudicar la UX ni el SEO
Un plan de implementación para añadir un chatbot a su sitio web manteniendo el recorrido del usuario, la velocidad de carga y la estructura de contenido en buen estado.

Cómo optimizar los tiempos de respuesta de un chatbot de IA: presupuesto de latencia, streaming y timeouts
Las respuestas rápidas de un chatbot se construyen a lo largo de toda la cadena técnica. Aprenda a planificar presupuestos de latencia, streaming, timeouts, reintentos y fallbacks seguros.

Probar un chatbot de IA en Shadow Mode: Paso seguro del prototipo al lanzamiento en la web
Mediante el Shadow Mode, puertas de calidad claras y un despliegue gradual, los equipos web prueban los chatbots de IA de forma segura antes del lanzamiento en producción.