pulsa ⌘K para cambiar de herramienta
CSP

Generador de Content Security Policy

Genera encabezados Content-Security-Policy.

local
csp-generator

Build a Content-Security-Policy. Common keywords: 'self' 'none' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' data: https: blob:

§01 ACERCA DE ESTA HERRAMIENTA

Descripción general

Una Content-Security-Policy le dice al navegador de qué orígenes puede cargar recursos. Su valor práctico es más estrecho de lo que sugiere el nombre: la CSP no arregla el cross-site scripting, lo contiene. Si una inyección se cuela por tu codificación de salida, una política estricta es lo que impide que el script inyectado se ejecute o que envíe lo que ha robado al servidor de un atacante.

La cabecera es una lista de directivas separadas por punto y coma, cada una con un tipo de recurso y los orígenes que se le permiten. Esta herramienta te da un campo por directiva, rellena una política de partida razonable y muestra la cabecera montada mientras escribes.

Cómo se usa

  1. Arranca desde la política prerrellenada: default-src 'self', object-src 'none', base-uri 'self', frame-ancestors 'none'. Es un suelo razonable para un sitio que sirve sus propios recursos.
  2. Añade los orígenes que de verdad necesitas, una directiva a la vez. Separa varios orígenes con espacios.
  3. Deja una directiva en blanco para omitirla; las directivas en blanco recurren a default-src.
  4. Copia la cabecera y despliégala primero como Content-Security-Policy-Report-Only. Lee los informes de violación, corrige la política y luego cambia al nombre de cabecera que aplica de verdad.

Las palabras clave de origen

Palabra claveSignificado
'self'El propio origen del documento: mismo esquema, host y puerto
'none'Nada en absoluto. Solo tiene sentido como único origen
'unsafe-inline'Permite <script> / <style> en línea y manejadores de eventos en línea
'unsafe-eval'Permite eval, new Function y argumentos de cadena en setTimeout
'strict-dynamic'Confía en los scripts creados por un script ya confiable. Hace que se ignoren las listas de hosts de esa directiva
data:Permite URI data:. Razonable en img-src, peligroso en script-src
https:Cualquier origen sobre HTTPS. Amplísimo: suele ser señal de que la política necesita afinarse
blob:Permite URL blob:, necesarias para Web Workers creados a partir de código generado

Los hashes ('sha256-…') y los nonces ('nonce-…') son las alternativas estrictas a 'unsafe-inline'. Un hash cubre un script en línea concreto cuyo contenido no cambia nunca, lo que encaja con los sitios generados estáticamente. Un nonce es un valor aleatorio nuevo en cada respuesta, lo que encaja con las páginas renderizadas en el servidor. No puedes usar un nonce en un hosting de archivos estáticos, porque no hay ningún paso por petición donde generarlo.

Las directivas que es fácil equivocar

base-uri no lo cubre default-src. Sin ella, una etiqueta <base href> inyectada puede redirigir todas las URL relativas de la página al host de un atacante —incluidas tus etiquetas de script— sin llegar a inyectar ningún script. Ponla en 'self' o en 'none'.

form-action tampoco la cubre default-src. Sin ella, un formulario inyectado puede enviar lo que escriban tus usuarios a otro origen.

frame-ancestors sustituye a X-Frame-Options. Cuando las dos se contradicen, los navegadores modernos siguen la CSP, así que poner solo la cabecera antigua deja a los navegadores más nuevos sin ninguna de las restricciones que querías expresar.

object-src 'none' vale la pena ponerlo de forma explícita. El contenido de plugins es una vía de ejecución heredada sin ningún uso legítimo en un sitio nuevo.

connect-src es la que se olvida todo el mundo. Gobierna fetch, XHR, los WebSockets y sendBeacon, que es exactamente por donde un script inyectado exfiltraría datos. Un script-src apretado con un connect-src abierto de par en par deja abierta la puerta que pretendía cerrar.

Ejemplos

Un sitio estático que sirve sus propios recursos y no tiene scripts de terceros:

default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none';
form-action 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:;
font-src 'self'; connect-src 'self'; upgrade-insecure-requests

El mismo sitio con una etiqueta de analítica, permitiendo solo los orígenes concretos que necesita en lugar de https::

script-src 'self' https://www.googletagmanager.com;
connect-src 'self' https://www.google-analytics.com

Recoger los informes de violación

El modo Report-Only solo sirve si lees los informes. Existen dos mecanismos, y el soporte de los navegadores está partido, así que un despliegue suele enviar los dos.

report-uri /csp-report es la directiva más antigua. Está obsoleta pero sigue siendo la de soporte más amplio, y publica un cuerpo JSON que describe el recurso bloqueado, la directiva que lo bloqueó y la URL del documento.

report-to es su sustituta. Nombra un grupo de informes configurado mediante una cabecera de respuesta Reporting-Endpoints aparte, lo que permite que un mismo endpoint reciba juntos los informes de CSP, de obsolescencia y de intervención.

Cualquiera que uses, cuenta con ruido. Las extensiones del navegador inyectan scripts y estilos en las páginas, y sus violaciones se ven idénticas a las tuyas en el informe. La señal es una violación que aparece para muchos usuarios distintos sobre la misma URL de documento y la misma directiva; un blocked-uri aislado que apunta a un esquema chrome-extension: es el gestor de contraseñas de alguien.

Ninguna de las dos directivas está disponible en una etiqueta <meta>. Los informes requieren la cabecera HTTP de verdad.

Notas

upgrade-insecure-requests reescribe las peticiones de subrecursos http:// a https:// antes de hacerlas. Es una ayuda para migrar páginas con contenido mixto, no un sustituto de arreglar las URL, y no se aplica a la navegación hacia otros sitios.

style-src suele ser el último 'unsafe-inline' en caer, y a menudo el que vale la pena conservar. Los atributos style en línea no se pueden cubrir con un hash —necesitarías 'unsafe-hashes' más un hash por cada valor de atributo distinto— y la inyección de CSS es un problema mucho más estrecho que la inyección de scripts. Gastar el esfuerzo primero en script-src es el mejor canje.

Despliega con Content-Security-Policy-Report-Only antes de aplicar la política de verdad. Una política que se pasa de estricta por una sola directiva no se degrada con elegancia: rompe la página en silencio para todos los visitantes cuyo navegador la aplique, y te enterarás por los usuarios y no por tus propias pruebas.

Si quieres comprobar qué envía ahora mismo un sitio en producción, el verificador de cabeceras HTTP muestra las cabeceras de respuesta de cualquier URL.

FAQ
¿Esto valida mi política?
No. Monta el texto de la cabecera a partir de los campos que rellenas; no comprueba que tus orígenes sean alcanzables ni que tu sitio siga funcionando. La única validación fiable es el modo Report-Only sobre tu sitio real, descrito más abajo.
¿Por qué se ignora 'unsafe-inline' cuando además añado un hash?
Eso es la especificación, no un error. Si un script-src contiene cualquier hash o nonce, los navegadores que los entienden ignoran 'unsafe-inline' por completo. Es una vía de actualización deliberada: los navegadores viejos se quedan con la palabra clave permisiva y los nuevos con la lista estricta.
¿Qué diferencia hay entre frame-src y frame-ancestors?
frame-src controla qué puede incrustar tu página. frame-ancestors controla quién puede incrustar tu página: es el sustituto en CSP de X-Frame-Options y, a diferencia de la mayoría de las directivas, no lo cubre default-src.
¿Necesito default-src si escribo todas las demás directivas?
Vale la pena ponerlo igualmente. default-src es el recurso de reserva para las directivas de descarga que no escribiste, incluidas las que se añadan a la especificación después de que publicaras tu sitio. Ponerlo en 'self' hace que una directiva futura falle cerrada en lugar de abierta.
¿Dónde pongo la cabecera terminada?
Como cabecera de respuesta HTTP, idealmente en el CDN o en el servidor web para que se aplique a todas las respuestas. Una etiqueta <meta http-equiv> también funciona, pero no puede expresar frame-ancestors ni report-uri, y solo surte efecto cuando el parser llega hasta ella.