Cómo proteger las aplicaciones Node.js con una política de seguridad de contenido

El autor seleccionó a la Free Software Foundation para recibir una donación como parte del programa Write for DOnations.
Introducción
Cuando el navegador carga una página, ejecuta una gran cantidad de código para reproducir el contenido. El código puede tener el mismo origen que el documento raíz o un origen diferente. De forma predeterminada, el navegador no distingue entre ambos y ejecuta cualquier código solicitado por una página, independientemente de la fuente. Los atacantes utilizan este exploit para inyectar de forma maliciosa secuencias de comandos en la página, que luego se ejecutan porque el navegador no tiene forma de determinar si el contenido es dañino. Estas situaciones son en las que una Política de seguridad de contenido (CSP) puede brindar protección.
Un CSP es un encabezado HTTP que proporciona una capa adicional de seguridad contra ataques de inyección de código, como secuencias de comandos entre sitios (XSS), clickjacking y otros ataques similares. Facilita la creación de una “lista de permitidos” de contenido confiable y bloquea la ejecución de código de fuentes que no están presentes en la lista de permitidos. También informa sobre cualquier violación de políticas a una URL de su elección, para que pueda mantenerse al tanto de posibles ataques de seguridad.
Con el encabezado CSP, puede especificar fuentes aprobadas para el contenido de su sitio que el navegador puede cargar. Se bloqueará la ejecución de cualquier código que no provenga de las fuentes aprobadas, lo que hace que sea considerablemente más difícil para un atacante inyectar contenido y extraer datos.
En este tutorial, revisará las diferentes protecciones que ofrece el encabezado CSP implementando una en una aplicación Node.js de ejemplo. También recopilará informes JSON de violaciones de CSP para detectar problemas y solucionar vulnerabilidades rápidamente.
Prerrequisitos
Para seguir este tutorial, necesitarás lo siguiente:
- Una versión reciente de Node.js instalada en su equipo. Siga los pasos del tutorial Cómo instalar Node.js correspondiente a su sistema operativo para configurar un entorno de desarrollo de Node.js.
También debe utilizar una versión reciente del navegador, preferiblemente Chrome, ya que tiene la mejor compatibilidad con las directivas de nivel 3 de CSP al momento de escribir este artículo (noviembre de 2020). Además, asegúrese de deshabilitar cualquier extensión de terceros mientras prueba la implementación de CSP para que no interfieran con los informes de infracciones que se muestran en la consola.
Paso 1: Configuración del proyecto de demostración
Para demostrar el proceso de creación de una Política de seguridad de contenido, trabajaremos en todo el proceso de implementación de una para este proyecto de demostración. Es un sitio web de una sola página con una variedad de contenido que se aproxima a un sitio web o aplicación típicos. Incluye una pequeña aplicación Vue.js, incrustaciones de YouTube y algunas imágenes provenientes de Unsplash. También utiliza fuentes de Google y el marco Bootstrap, que se carga a través de una red de distribución de contenido (CDN).
En este paso, configurará el proyecto de demostración en su servidor de prueba o máquina local y lo verá en su navegador.
Primero, clone el proyecto en su sistema de archivos usando el siguiente comando:
- git clone https://github.com/do-community/csp-demo
Una vez configurado el directorio del proyecto, cámbielo con el siguiente comando:
- cd csp-demo
A continuación, instale las dependencias especificadas en el package.json
archivo con el siguiente comando. Utilice el express
paquete para configurar el servidor web, mientras que nodemon
ayuda a reiniciar automáticamente la aplicación de nodo cuando detecta cambios de archivo en el directorio:
- npm install
Una vez que haya instalado las dependencias, ingrese el siguiente comando para iniciar el servidor web en el puerto 5500
:
- npm start
Ahora puede visitar your_server_ip:5500
o localhost:5500
en su navegador para ver la página de demostración. Encontrará el texto ¡Hola mundo!, una inserción de YouTube y algunas imágenes en la página.
En la siguiente sección, implementaremos una política de CSP que cubra solo las protecciones más básicas. Luego, desarrollaremos esa política en las secciones siguientes a medida que descubramos todos los recursos legítimos que debemos permitir en la página.
Paso 2: Implementación de un CSP básico
Vamos a escribir una política CSP que restrinja las fuentes, imágenes, scripts, estilos e incrustaciones a aquellos que se originan únicamente en el host actual. El siguiente es el encabezado de respuesta que logra esto:
Content-Security-Policy: default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self';
A continuación se muestra una explicación de las directivas de política en este encabezado:
font-src
define las fuentes desde donde se pueden cargar las fuentes.img-src
define las fuentes desde las que se permite la carga de imágenes.script-src
controla cualquier privilegio de carga de scripts en una página web.style-src
Es la directiva para permitir fuentes de hojas de estilo.frame-src
Define las fuentes permitidas para incrustaciones de marcos. (Quedó obsoleto en el nivel 2 de CSP, pero se restableció en el nivel 3).default-src
define una política de respaldo para ciertas directivas si no se especifican explícitamente en el encabezado. Aquí hay una lista completa de las directivas a las que se recurredefault-src
.
En este ejemplo, a todas las directivas especificadas se les asigna la 'self'
palabra clave en su lista de fuentes. Esto indica que solo se debe permitir la ejecución de recursos del host actual (incluido el esquema de URL y el número de puerto). Por ejemplo, script-src 'self'
permite la ejecución de scripts desde el host actual, pero bloquea todas las demás fuentes de scripts.
Sigamos adelante y agreguemos el encabezado a nuestro proyecto Node.js.
Deje su aplicación ejecutándose y abra una nueva ventana de terminal para trabajar con su server.js
archivo:
- nano server.js
A continuación, agregue el encabezado CSP del ejemplo en una capa de middleware de Express. Esto garantiza que incluya el encabezado en cada respuesta del servidor:
servidor.js
const express = require('express');const bodyParser = require('body-parser');const path = require('path');const app = express();app.use(function (req, res, next) { res.setHeader( 'Content-Security-Policy', "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self'" ); next();});app.use(bodyParser.json());app.use(express.static(path.join(__dirname)));app.get('/', (req, res) = { res.sendFile(path.join(__dirname + '/index.html'));});const server = app.listen(process.env.PORT || 5500, () = { const { port } = server.address(); console.log(`Server running on PORT ${port}`);});
Guarda el archivo y vuelve a cargar el proyecto en tu navegador. Notarás que la página está completamente rota.
Nuestro encabezado CSP funciona como se esperaba y se ha bloqueado la carga de todas las fuentes externas que incluimos en la página porque violan la política definida. Sin embargo, esta no es la forma ideal de probar una política completamente nueva, ya que puede dañar un sitio web cuando se producen violaciones.
Por eso Content-Security-Policy-Report-Only
existe el encabezado. Puedes usarlo en lugar de Content-Security-Policy
para evitar que el navegador aplique la política, a la vez que sigues informando de las infracciones que se producen, lo que significa que puedes perfeccionar la política sin poner en riesgo tu sitio. Una vez que estés conforme con tu política, puedes volver al encabezado de aplicación para que se activen las protecciones.
Continúe y reemplace el Content-Security-Policy
encabezado con Content-Security-Policy-Report-Only
en su server.js
archivo:
- nano server.js
Añade el siguiente código resaltado:
servidor.js
. . .app.use(function (req, res, next) { res.setHeader( 'Content-Security-Policy-Report-Only', "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self'" ); next();});. . .
Guarde el archivo y vuelva a cargar la página en su navegador. La página vuelve a funcionar, pero la consola del navegador sigue informando las infracciones de CSP. Cada infracción tiene el prefijo [Report Only]
para indicar que la política no se aplica.
En esta sección, creamos la implementación inicial de nuestro CSP y la configuramos en modo de solo informes para que podamos refinarla sin provocar fallas en el sitio. En la siguiente sección, solucionaremos las infracciones que se generaron a través de nuestro CSP inicial.
Paso 3: Solución de las infracciones de las políticas
La política que implementamos en la sección anterior desencadenó varias violaciones porque restringimos todos los recursos solo al origen; sin embargo, tenemos varios activos de terceros en la página.
Las dos formas de solucionar las infracciones de CSP son: aprobar las fuentes en la política o eliminar el código que desencadena las infracciones. Dado que los recursos legítimos desencadenan todas las infracciones, en esta sección nos concentraremos principalmente en la primera opción.
Abra la consola de su navegador. Se mostrarán todas las infracciones actuales del CSP. Solucionemos cada uno de estos problemas.
Permitir las hojas de estilo
Las dos primeras infracciones en la consola son de las fuentes de Google y las hojas de estilo de Bootstrap, que estás cargando desde https://fonts.googleapis.com
y https://cdn.jsdelivr.net
respectivamente. Puedes permitirlas en la página mediante la style-src
directiva:
style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net;
Esto especifica que los archivos CSS del host de origen, https://fonts.googleapis.com
y https://cdn.jsdelivr.net
, deben ejecutarse en la página. Esta política es bastante amplia, porque permite cualquier hoja de estilo de los dominios de la lista de permitidos (no solo los que estás usando actualmente).
Podemos ser más específicos utilizando el archivo o directorio exacto que nos gustaría permitir:
style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css;
Ahora, solo permitirá que se ejecute la hoja de estilo exacta y especificada. Bloqueará todas las demás hojas de estilo, incluso si se originan en https://cdn.jsdelivr.net
.
Puede actualizar el encabezado CSP de la siguiente manera con la style-src
directiva actualizada. Cuando vuelva a cargar la página, se habrán resuelto ambas infracciones:
servidor.js
. . .app.use(function (req, res, next) { res.setHeader( 'Content-Security-Policy-Report-Only', "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self';" ); next();});. . .
Permitir las fuentes de imágenes
Las imágenes que estás usando en la página provienen de una única fuente: https://images.unsplash.com
. Permitámoslo mediante la img-src
directiva de la siguiente manera:
servidor.js
. . .app.use(function (req, res, next) { res.setHeader( 'Content-Security-Policy-Report-Only', "default-src 'self'; font-src 'self'; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self'" ); next();});. . .
Permitir la incrustación de YouTube
Puede permitir fuentes válidas para contextos de navegación anidados, que utilizan elementos como iframe
, mediante la frame-src
directiva. Si esta directiva no está presente, el navegador buscará la child-src
directiva y, posteriormente, recurrirá a default-src
ella.
Nuestra política actual limita las incrustaciones de fotogramas al host de origen. Agreguemos contenido https://www.youtube.com
a la lista de permitidos para que el CSP no bloquee la carga de incrustaciones de YouTube una vez que apliquemos la política:
frame-src 'self' https://www.youtube.com;
Tenga en cuenta que el www
subdominio es importante aquí. Si tiene una incrustación de https://youtube.com
, se bloqueará de acuerdo con esta política a menos que también la agregue https://youtube.com
a la lista de permitidos:
frame-src 'self' https://www.youtube.com https://youtube.com;
Aquí está el encabezado CSP actualizado. Cambie la frame-src
directiva de la siguiente manera:
servidor.js
. . .app.use(function (req, res, next) { res.setHeader( 'Content-Security-Policy-Report-Only', "default-src 'self'; font-src 'self'; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;" ); next();});. . .
Permitir los archivos de fuentes
La hoja de estilos de fuentes de Google contiene referencias a varios archivos de fuentes de https://fonts.gstatic.com
. Verá que estos archivos actualmente infringen la font-src
política definida (actualmente 'self'
), por lo que debe tenerlos en cuenta en su política revisada:
font-src 'self' https://fonts.gstatic.com;
Actualice la font-src
directiva en el encabezado CSP de la siguiente manera:
servidor.js
. . .app.use(function (req, res, next) { res.setHeader( 'Content-Security-Policy-Report-Only', "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;" ); next();});. . .
Una vez que vuelva a cargar la página, la consola ya no informará violaciones en los archivos de fuentes de Google.
Permitir el script Vue.js
Un script de Vue.js cargado a través de una CDN está mostrando el texto ¡Hola mundo! en la parte superior de la página. Permitiremos su ejecución en la página a través de la script-src
directiva. Como se mencionó anteriormente, es importante ser específico al permitir fuentes de CDN para no abrir nuestro sitio a otros posibles scripts maliciosos que estén alojados en ese dominio.
script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js;
En este ejemplo, solo permites que se ejecute en la página el script exacto en esa URL. Si quieres cambiar entre compilaciones de desarrollo y producción, también tendrás que agregar la otra URL del script a la lista de permitidos, o puedes permitir todos los recursos presentes en la /dist
ubicación para cubrir ambos casos a la vez:
script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/;
Aquí está el encabezado CSP actualizado con los cambios relevantes:
servidor.js
. . .app.use(function (req, res, next) { res.setHeader( 'Content-Security-Policy-Report-Only', "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;" ); next();});. . .
En este punto, hemos permitido con éxito todos los archivos y scripts externos de los que depende nuestra página. Pero aún tenemos una infracción de CSP más que resolver debido a la presencia de un script en línea en la página. Exploraremos algunas soluciones a este problema en la siguiente sección.
Paso 4: Manejo de fuentes en línea
Si bien es posible aprobar código en línea (como código JavaScript en una script
etiqueta) dentro de un CSP usando la 'unsafe-inline'
palabra clave, no se recomienda porque aumenta en gran medida el riesgo de un ataque de inyección de código.
Esta política de ejemplo permite la ejecución de cualquier script en línea en la página, pero esto no es seguro por las razones mencionadas anteriormente.
script-src 'self' 'unsafe-inline' https://unpkg.com/vue@3.0.2/dist/;
La mejor forma de evitar su uso unsafe-inline
es mover el código en línea a un archivo externo y hacer referencia a él de esa manera. Este es un mejor enfoque para el almacenamiento en caché, la minimización y la capacidad de mantenimiento, y también hace que el CSP sea más fácil de modificar en el futuro.
Sin embargo, si es absolutamente necesario utilizar código en línea, hay dos formas principales de agregarlo a su lista de permitidos de forma segura.
Opción 1: usar un hash
Este método requiere que calcules un hash SHA basado en el script en sí y luego lo agregues a la script-src
directiva. En las versiones recientes de Chrome, ni siquiera necesitas generar el hash tú mismo, ya que ya está incluido en el error de violación de CSP en la consola:
[Report Only] Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' https://unpkg.com/vue@3.0.2/dist/". Either the 'unsafe-inline' keyword, a hash ('sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='), or a nonce ('nonce-...') is required to enable inline execution.
La sección resaltada aquí es el hash SHA256 exacto que necesitaría agregar a la script-src
directiva para permitir la ejecución del script en línea específico que desencadenó la violación.
Copia el hash y agrégalo a tu CSP de la siguiente manera:
script-src 'self' https://unpkg.com/vue@3.0.2/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM=';
La desventaja de este enfoque es que si el contenido del script cambia, el hash generado será diferente, lo que provocará una violación.
Opción 2: usar un Nonce
La segunda forma de permitir la ejecución de código en línea es mediante un nonce. Se trata de cadenas aleatorias que se pueden utilizar para permitir un bloque completo de código independientemente de su contenido.
A continuación se muestra un ejemplo de un valor nonce en uso:
script-src 'self' https://unpkg.com/vue@3.0.2/dist/ 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
El valor del nonce en el CSP debe coincidir con el nonce
atributo en el script:
script nonce="EDNnf03nceIOfn39fn3e9h3sdfa" // Some inline code/script
Los nonces deben ser indescifrables y deben generarse dinámicamente cada vez que se carga la página para que un atacante no pueda usarlos para ejecutar un script malicioso. Si decide implementar esta opción, puede usar el crypto
paquete para generar un nonce de la siguiente manera:
const crypto = require('crypto');let nonce = crypto.randomBytes(16).toString('base64');
Optaremos por el método hash en este tutorial porque es más práctico para nuestro caso de uso.
Actualice la script-src
directiva en su encabezado CSP para incluir el hash SHA256 del único script en línea como se muestra a continuación:
servidor.js
. . .app.use(function (req, res, next) { res.setHeader( 'Content-Security-Policy-Report-Only', "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://unpkg.com/vue@3.0.2/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;" ); next();});. . .
Esto elimina el error de violación de CSP final que el script en línea activa desde la consola.
En la siguiente sección, monitorearemos los efectos de nuestro CSP en un entorno de producción.
Paso 5: Seguimiento de las infracciones
Una vez que tenga su CSP en funcionamiento, es necesario vigilar sus efectos una vez que esté en uso. Por ejemplo, si olvida permitir una fuente legítima en producción o cuando un atacante intenta explotar un vector de ataque XSS (que debe identificar y detener de inmediato).
Sin algún tipo de informe activo, no hay forma de saber de estos eventos. Por eso report-to
existe la directiva. Especifica una ubicación a la que el navegador debe enviar un informe de infracción en formato JSON, en caso de que tenga que tomar medidas en función del CSP.
Para utilizar esta directiva, debe agregar un encabezado adicional para especificar un punto final para la API de informes:
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"http://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}
Una vez configurado esto, especifique el nombre del grupo en la report-to
directiva de la siguiente manera:
report-to csp-endpoint;
Aquí se muestra la parte actualizada del server.js
archivo con los cambios. Asegúrese de reemplazar el your_server_ip
marcador de posición en el Report-To
encabezado con la dirección IP real de su servidor:
servidor.js
. . .app.use(function (req, res, next) { res.setHeader( 'Report-To', '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}' ); res.setHeader( 'Content-Security-Policy-Report-Only', "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint;" ); next();});. . .
La report-to
directiva pretende reemplazar a la report-uri
directiva ahora obsoleta, pero la mayoría de los navegadores aún no la admiten (a fecha de noviembre de 2020). Por lo tanto, para garantizar la compatibilidad con los navegadores actuales y, al mismo tiempo, la compatibilidad con las futuras versiones de navegadores, debe especificar tanto report-uri
y report-to
en su CSP. Si se admite la última, se ignorará la primera:
servidor.js
. . .app.use(function (req, res, next) { res.setHeader( 'Report-To', '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}' ); res.setHeader( 'Content-Security-Policy-Report-Only', "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint; report-uri /__cspreport__;" ); next();});. . .
La /__cspreport__
ruta también debe existir en el servidor; agregue esto a su archivo de la siguiente manera:
servidor.js
. . .app.get('/', (req, res) = { res.sendFile(path.join(__dirname + '/index.html'));});app.post('/__cspreport__', (req, res) = { console.log(req.body);});. . .
Algunos navegadores envían la Content-Type
carga útil del informe como application/csp-report
, mientras que otros utilizan application/json
. Si report-to
se admite la directiva, Content-Type
debería ser application/reports+json
.
Para tener en cuenta todos los Content-Type
valores posibles, debes configurar algunas configuraciones en tu servidor express:
servidor.js
. . .app.use( bodyParser.json({ type: ['application/json', 'application/csp-report', 'application/reports+json'], }));. . .
En este punto, cualquier violación de CSP se enviará a la /__cspreport__
ruta y posteriormente se registrará en la terminal.
Puede probarlo agregando un recurso de una fuente que no sea compatible con el CSP actual o modificando el script en línea en el index.html
archivo como se muestra a continuación:
índice.html
. . .script new Vue({ el: '#vue', render(createElement) { return createElement('h1', 'Hello World!'); }, }); console.log("Hello")/script. . .
Esto provocará una violación porque el hash del script ahora es diferente de lo que incluiste en el encabezado CSP.
A continuación se muestra un informe de infracción típico de un navegador que utiliza report-uri
:
{ 'csp-report': { 'document-uri': 'http://localhost:5500/', referrer: '', 'violated-directive': 'script-src-elem', 'effective-directive': 'script-src-elem', 'original-policy': "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-uri /__cspreport__;", disposition: 'report', 'blocked-uri': 'inline', 'line-number': 58, 'source-file': 'http://localhost:5500/', 'status-code': 200, 'script-sample': '' }}
Las partes de este informe son:
document-uri
:La página en la que ocurrió la violación.referrer
:El referente de la página.blocked-uri
:El recurso que violó la política de la página (uninline
script en este caso).line-number
:El número de línea donde comienza el código en línea.violated-directive
:La directiva específica que se violó. (script-src-elem
en este caso, que retrocede ascript-src
.)original-policy
:La política completa de la página.
Si el navegador admite la report-to
directiva, la carga útil debería tener una estructura similar a la que se muestra a continuación. Observe cómo se diferencia de la report-uri
carga útil, aunque sigue conteniendo la misma información:
[{"age": 16796,"body": {"blocked-uri": "https://vimeo.com","disposition": "enforce","document-uri": "https://localhost:5500/","effective-directive": "frame-src","line-number": 58, 'original-policy': "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-uri /__cspreport__;","referrer": "","script-sample": "","sourceFile": "https://localhost:5500/","violated-directive": "frame-src"},"type": "csp","url": "https://localhost:5500/","user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"}]
Nota : La report-to
directiva solo se admite en contextos seguros, lo que significa que debe configurar su servidor Express con un certificado HTTPS válido; de lo contrario, no podrá probarlo ni usarlo.
En esta sección, configuramos correctamente la supervisión de CSP en nuestro servidor para poder detectar y solucionar problemas rápidamente. Sigamos adelante y finalicemos este tutorial aplicando la política final en el siguiente paso.
Paso 6 — Publicación de la política final
Una vez que esté seguro de que su CSP está configurado correctamente (idealmente después de dejarlo en producción durante algunos días o semanas en modo de solo informes), puede aplicarlo cambiando el encabezado de CSP de Content-Security-Policy-Report-Only
a Content-Security-Policy
.
Además de informar las violaciones, esto evitará que se ejecuten recursos no autorizados en la página, lo que generará una experiencia más segura para sus visitantes.
Aquí está la versión final del server.js
archivo:
servidor.js
const express = require('express');const bodyParser = require('body-parser');const path = require('path');const app = express();app.use(function (req, res, next) { res.setHeader( 'Report-To', '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"http://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}' ); res.setHeader( 'Content-Security-Policy', "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint; report-uri /__cspreport__;" ); next();});app.use( bodyParser.json({ type: [ 'application/json', 'application/csp-report', 'application/reports+json', ], }));app.use(express.static(path.join(__dirname)));app.get('/', (req, res) = { res.sendFile(path.join(__dirname + '/index.html'));});app.post('/__cspreport__', (req, res) = { console.log(req.body);});const server = app.listen(process.env.PORT || 5500, () = { const { port } = server.address(); console.log(`Server running on PORT ${port}`);});
Compatibilidad con navegadores El encabezado CSP es compatible con todos los navegadores, con excepción de Internet Explorer, que utiliza el X-Content-Security-Poli
Deja una respuesta