Secuencia de comandos entre sitios (XSS), la capacidad de inyectar secuencias de comandos maliciosas en una aplicación web ha sido uno de los las más grandes vulnerabilidades de seguridad web desde hace más de una década.
Política de Seguridad del Contenido (CSP)
es una capa adicional de seguridad que ayuda a mitigar el XSS. Para configurar un CSP,
agrega el encabezado HTTP Content-Security-Policy
a una página web y establece valores
controlar qué recursos puede cargar el usuario-agente para esa página.
En esta página, se explica cómo usar un CSP basado en nonces o hashes para mitigar XSS. en lugar de los CSP basados en listas de entidades permitidas de host de uso general que a menudo salen de la página. Se exponen a XSS porque se pueden omitir en la mayoría de las configuraciones.
Término clave: un nonce es un número al azar usado solo una vez y que se puede usar para marcar un
La etiqueta <script>
es de confianza.
Término clave: una función hash es una función matemática que convierte una entrada
de salida en un valor numérico comprimido llamado hash. Puedes usar un hash
(por ejemplo, SHA-256) para marcar un
La etiqueta <script>
es de confianza.
Una Política de Seguridad del Contenido basada en nonces o hashes suele denominarse CSP estricta. Cuando una aplicación usa una CSP estricta, los atacantes que encuentran HTML por lo general, no se pueden usar para forzar la ejecución del navegador de comandos maliciosas en documentos vulnerables. Esto se debe a que una CSP estricta permite secuencias de comandos con codificación hash o secuencias de comandos con el valor correcto de nonce generado en el por lo que los atacantes no pueden ejecutar la secuencia de comandos sin conocer el nonce correcto para una respuesta dada.
¿Por qué deberías usar una CSP estricta?
Si tu sitio ya tiene una CSP similar a script-src www.googleapis.com
,
lo que probablemente no sea eficaz
contra sitios múltiples. Este tipo de CSP se denomina
CSP en la lista de entidades permitidas. Requieren mucha personalización y se pueden
evitadas por los atacantes.
Las CSP estrictas basadas en nonces o hashes criptográficos evitan estas dificultades.
Estructura estricta de la CSP
Una política de seguridad del contenido estricta básica usa una de las siguientes respuestas HTTP encabezados:
CSP estricta basada en nonce
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
![](https://web.developers.google.cn/static/articles/strict-csp/image/er4BaGCJzBwDaESFKfZd.jpg?authuser=7&hl=es-419)
CSP estricta basada en hash
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Las siguientes propiedades hacen que una CSP como esta sea "estricta" y, por lo tanto, proteger:
- Usa nonces
'nonce-{RANDOM}'
o hashes'sha256-{HASHED_INLINE_SCRIPT}'
para indicar en qué etiquetas<script>
confía el desarrollador del sitio. el navegador del usuario. - Establece
'strict-dynamic'
para reducir el esfuerzo de implementar una CSP basada en nonce o hash lo que permite la ejecución de secuencias de comandos creadas por una secuencia de comandos confiable. Esto también desbloquea el uso de la mayoría de las bibliotecas y widgets de JavaScript de terceros. - No se basa en listas de URLs permitidas, por lo que no omisiones de CSP comunes.
- Bloquea secuencias de comandos intercaladas que no son de confianza, como los controladores de eventos intercalados o
javascript:
URIs. - Restringe
object-src
para inhabilitar complementos peligrosos, como Flash. - Restringe
base-uri
para bloquear la inserción de etiquetas<base>
. Esto evita que los atacantes cambien la ubicación de las secuencias de comandos cargadas desde las URL relativas.
Adopta una CSP estricta
Para adoptar una CSP estricta, debes hacer lo siguiente:
- Decide si tu aplicación debe establecer una CSP basada en nonce o hash.
- Copia la CSP de la sección Estructura estricta de la CSP y configúrala. como encabezado de respuesta en tu aplicación.
- Refactoriza las plantillas HTML y el código del cliente para eliminar los patrones que se incompatibles con CSP.
- Implementa tu CSP.
Puedes usar Lighthouse
(v7.3.0 y versiones posteriores con la marca --preset=experimental
) Auditoría de prácticas recomendadas
a lo largo de este proceso para verificar si tu sitio tiene un CSP
lo suficientemente estrictos como para
ser eficaz contra XSS.
![Faro
un informe con una advertencia que indica que no se encuentra ninguna CSP en el modo de aplicación forzosa.](https://web.developers.google.cn/static/articles/strict-csp/image/lighthouse-report-warning-74f66e2ef76dc.png?authuser=7&hl=es-419)
Paso 1: Decide si necesitas una CSP basada en nonce o hash
Así funcionan los dos tipos de CSP estricta:
CSP basada en nonce
Con una CSP basada en nonce, generas un número al azar en el tiempo de ejecución y lo incluyes en la CSP y asociarla con cada etiqueta de secuencia de comandos de tu página. Un atacante no pueden incluir ni ejecutar una secuencia de comandos maliciosa en tu página, ya que tendrían que adivina el número al azar correcto para esa secuencia de comandos. Esto solo funciona si el número no se puede adivinar y se genera recientemente en el tiempo de ejecución para cada respuesta.
Usa una CSP basada en nonce para las páginas HTML renderizadas en el servidor. En estas páginas, puedes crear un nuevo número al azar para cada respuesta.
CSP basada en hash
Para una CSP basada en hash, el hash de cada etiqueta de secuencia de comandos intercalada se agrega a la CSP. Cada secuencia de comandos tiene un hash diferente. Un atacante no puede incluir ni ejecutar una aplicación secuencia de comandos en tu página, porque el hash de esa secuencia tendría que estar en el CSP para que se ejecute.
Usa una CSP basada en hash para páginas HTML publicadas de forma estática o aquellas que se deben se almacenó en caché. Por ejemplo, puedes usar una CSP basada en hash para sitios web aplicaciones compiladas con frameworks, como Angular, React y otros, que se se entrega estáticamente sin renderización del servidor.
Paso 2: Establece una CSP estricta y prepara tus secuencias de comandos
Cuando configuras una CSP, tienes las siguientes opciones:
- Modo de solo informes (
Content-Security-Policy-Report-Only
) o modo de aplicación (Content-Security-Policy
). En el modo solo informe, el CSP no bloqueará recursos aún, por lo que no se rompe en tu sitio, pero puedes ver errores y obtener informes para cualquier elemento que se hubiera bloqueado. A nivel local, cuando configurar tu CSP, esto no importa porque ambos modos te muestran errores en la consola del navegador. En todo caso, el modo de aplicación te puede ayudar a encontrar recursos que bloquea tu CSP en borrador, ya que bloquear un recurso puede la página se vea dañada. El modo de solo informes se vuelve más útil más adelante en el proceso. (consulta el Paso 5). - Encabezado o etiqueta HTML
<meta>
. Para el desarrollo local, una etiqueta<meta>
puede ser más conveniente para modificar tu CSP y ver rápidamente cómo afecta a tu sitio. Sin embargo, ten en cuenta lo siguiente:- Más adelante, cuando implementes tu CSP en producción, te recomendamos configurarlo como un encabezado HTTP.
- Si quieres configurar tu CSP en modo de solo informes, deberás establecerlo como encabezado, ya que las metaetiquetas de la CSP no admiten el modo de solo informes.
Configurar la siguiente respuesta HTTP Content-Security-Policy
encabezado en tu aplicación:
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
Genera un nonce para CSP
Un nonce es un número al azar que se usa solo una vez por carga de página. Un nonce basado en La CSP solo puede mitigar XSS si los atacantes no pueden adivinar el valor del nonce. R El nonce de la CSP debe cumplir con lo siguiente:
- Un valor aleatorio criptográficamente seguro (idealmente, más de 128 bits de longitud)
- Recientemente generado para cada respuesta
- Codificación en base64
Estos son algunos ejemplos de cómo agregar un nonce de CSP en frameworks del servidor:
- Django (python)
- Express (JavaScript):
const app = express(); app.get('/', function(request, response) { // Generate a new random nonce value for every response. const nonce = crypto.randomBytes(16).toString("base64"); // Set the strict nonce-based CSP response header const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`; response.set("Content-Security-Policy", csp); // Every <script> tag in your application should set the `nonce` attribute to this value. response.render(template, { nonce: nonce }); });
Agrega un atributo nonce
a los elementos <script>
Con una CSP basada en nonce, cada elemento <script>
debe
tienen un atributo nonce
que coincide con el nonce aleatorio
de salida especificado en el encabezado de CSP. Todas las secuencias de comandos pueden tener el mismo
nonce. El primer paso es agregar estos atributos
a todas las secuencias de comandos
CSP lo permite.
Configurar la siguiente respuesta HTTP Content-Security-Policy
encabezado en tu aplicación:
Content-Security-Policy: script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
Para varias secuencias de comandos intercaladas, la sintaxis es la siguiente:
'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}'
Cargue secuencias de comandos originadas de forma dinámica
Debido a que los hashes de la CSP son compatibles con los navegadores solo para secuencias de comandos intercaladas, debe cargar todas las secuencias de comandos de terceros de forma dinámica mediante una secuencia de comandos intercalada. Los hashes para secuencias de comandos de origen no son compatibles con todos los navegadores.
![](https://web.developers.google.cn/static/articles/strict-csp/image/B2YsfJDYw8PRI6kJI7Bs.jpg?authuser=7&hl=es-419)
<script> var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js']; scripts.forEach(function(scriptUrl) { var s = document.createElement('script'); s.src = scriptUrl; s.async = false; // to preserve execution order document.head.appendChild(s); }); </script>Para permitir que se ejecute esta secuencia de comandos, debes calcular el hash de la secuencia de comandos intercalada. y agregarlo al encabezado de respuesta de CSP, reemplazando el
{HASHED_INLINE_SCRIPT}
marcador de posición. Para reducir la cantidad de hashes, puedes combinarlos todos intercalados
secuencias de comandos en una sola. Para ver esto en acción, consulta este
ejemplo
y su código.
<script src="https://example.org/foo.js"></script> <script src="https://example.org/bar.js"></script>La CSP bloquea estas secuencias de comandos porque solo las secuencias de comandos intercaladas pueden generar un hash.
Consideraciones sobre la carga de secuencias de comandos
El ejemplo de secuencia de comandos intercalada agrega s.async = false
para garantizar que
que foo
se ejecuta antes de bar
, incluso si
bar
se carga primero. En este fragmento, s.async = false
no bloquea el analizador mientras se cargan las secuencias de comandos, ya que
se agregan de forma dinámica. El analizador se detiene solo mientras se ejecutan las secuencias de comandos, ya que
que lo haría para async
secuencias de comandos. Sin embargo, con este fragmento,
ten en cuenta:
-
Una o ambas secuencias de comandos pueden ejecutarse antes de que el documento termine
descargas. Si deseas que el documento esté listo para el momento en que
se ejecuten, espera el evento
DOMContentLoaded
antes de anexas las secuencias de comandos. Si esto provoca un problema de rendimiento porque el Las secuencias de comandos no comienzan a descargarse con suficiente anticipación, usa las etiquetas de precarga que aparece más temprano en la página. -
defer = true
no realiza ninguna acción. Si lo necesitas tu rendimiento, ejecuta la secuencia de comandos manualmente cuando sea necesario.
Paso 3: Refactoriza las plantillas HTML y el código del cliente
Controladores de eventos intercalados (como onclick="…"
y onerror="…"
) y URI de JavaScript
(<a href="javascript:…">
) se puede usar para ejecutar secuencias de comandos. Esto significa que se
un atacante que encuentra un error XSS puede inyectar este tipo de HTML y ejecutar
JavaScript: Una CSP basada en nonce o hash prohíbe el uso de este tipo de lenguaje de marcado.
Si tu sitio usa alguno de estos patrones, deberás refactorizarlos para que sean
alternativas.
Si habilitaste la CSP en el paso anterior, podrás ver los incumplimientos de la CSP en la consola cada vez que CSP bloquea un patrón incompatible.
![Informes de incumplimiento de la CSP en la consola para desarrolladores de Chrome](https://web.developers.google.cn/static/articles/strict-csp/image/csp-violation-reports-th-ff599b24b17be.jpg?authuser=7&hl=es-419)
En la mayoría de los casos, la solución es sencilla:
Cómo refactorizar controladores de eventos intercalados
<span id="things">A thing.</span> <script nonce="${nonce}"> document.getElementById('things').addEventListener('click', doThings); </script>CSP permite controladores de eventos que se registran con JavaScript.
<span onclick="doThings();">A thing.</span>La CSP bloquea los controladores de eventos intercalados.
Refactoriza los URIs de javascript:
<a id="foo">foo</a> <script nonce="${nonce}"> document.getElementById('foo').addEventListener('click', linkClicked); </script>CSP permite controladores de eventos que se registran con JavaScript.
<a href="javascript:linkClicked()">foo</a>La CSP bloquea JavaScript: los URIs.
Quitar eval()
de JavaScript
Si tu aplicación usa eval()
para convertir serializaciones de cadenas JSON en JS
debes refactorizar esas instancias en JSON.parse()
, que también es
más rápido.
Si no puedes quitar todos los usos de eval()
, aún puedes establecer un modelo estricto basado en nonce
pero debes usar la palabra clave de CSP 'unsafe-eval'
, lo que hace que tu
un poco menos segura.
Puedes encontrar estos y más ejemplos de esta refactorización en esta CSP estricta codelab:
Paso 4 (opcional): Agrega resguardos para admitir versiones anteriores del navegador
Si necesitas ofrecer compatibilidad con versiones anteriores del navegador, haz lo siguiente:
- Para usar
strict-dynamic
, debes agregarhttps:
como resguardo para versiones anteriores más recientes de Safari. Cuando lo hagas:- Todos los navegadores compatibles con
strict-dynamic
ignoran el resguardo dehttps:
. por lo que esto no reducirá la solidez de la política. - En navegadores antiguos, las secuencias de comandos de origen externo solo pueden cargarse si provienen de
un origen HTTPS. Esto es menos seguro que una CSP estricta, pero
evita algunas causas comunes de XSS, como inyecciones de URI de
javascript:
.
- Todos los navegadores compatibles con
- Para garantizar la compatibilidad con versiones de navegador muy antiguas (más de 4 años), puedes agregar
unsafe-inline
como resguardo Todos los navegadores recientes ignoranunsafe-inline
. si hay un nonce o un hash de la CSP.
Content-Security-Policy:
script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
object-src 'none';
base-uri 'none';
Paso 5: Implementa tu CSP
Luego de confirmar que tu CSP no bloquea ninguna secuencia de comandos legítima en tu desarrollo local, puedes implementar tu CSP en la etapa de pruebas y, luego, en entorno de producción:
- (Opcional) Implementa tu CSP en modo de solo informes con el
Encabezado
Content-Security-Policy-Report-Only
. El modo de solo informes es útil para probar un cambio potencialmente rotundo, como un CSP nuevo en producción, antes de comenzar a aplicar restricciones de CSP. En el modo de solo informes, el CSP afectan el comportamiento de tu app, pero el navegador aún genera errores de consola e informes de incumplimiento cuando encuentra patrones incompatibles con tu CSP para que puedas ver qué habría fallado para tus usuarios finales. Para ver más consulta API de Reporting. - Cuando estés seguro de que el CSP no interrumpirá el sitio para los usuarios finales,
implementa tu CSP con el encabezado de respuesta
Content-Security-Policy
. Mié recomienda configurar la CSP con un encabezado HTTP del lado del servidor, ya que es más seguro que una etiqueta<meta>
. Después de que completes este paso, tu CSP comenzará proteger tu app de XSS.
Limitaciones
Por lo general, una CSP estricta proporciona una capa adicional de seguridad sólida que ayuda a
mitigar el XSS. En la mayoría de los casos, el CSP reduce la superficie de ataque de manera significativa, al
Se rechazan los patrones peligrosos, como los URIs de javascript:
. Sin embargo, según el tipo
de CSP que usas (nonces, hashes, con o sin 'strict-dynamic'
), puede
hay casos en los que CSP tampoco protege tu app:
- Si no aprovechas una secuencia de comandos, pero sí hay una inyección directamente en el cuerpo o el
El parámetro
src
de ese elemento<script>
. - Si hay inyecciones en las ubicaciones de las secuencias de comandos creadas dinámicamente
(
document.createElement('script')
), incluso en cualquier función de biblioteca que creanscript
nodos del DOM según los valores de sus argumentos. Esta incluye algunas APIs comunes, como.html()
de jQuery y.get()
y.post()
en jQuery < 3.0 - Si hay inyecciones de plantillas en aplicaciones antiguas de AngularJS. Un atacante que se puede insertar en una plantilla de AngularJS, ejecutar JavaScript arbitrario.
- Si la política contiene
'unsafe-eval'
, las inyecciones eneval()
,setTimeout()
y algunas otras APIs que se usan con poca frecuencia.
Los desarrolladores e ingenieros de seguridad deben prestar especial atención a estos de patrones de seguridad durante las revisiones de código y las auditorías de seguridad. Puedes encontrar más detalles en estos casos en Política de Seguridad del Contenido: Un lío exitoso entre el endurecimiento y la mitigación.
Lecturas adicionales
- CSP murió, ¡que viva la CSP! Sobre la inseguridad de las listas blancas y el futuro de la política de seguridad del contenido
- Evaluador de CSP
- Conferencia de LocoMoco: Política de Seguridad del Contenido - Una mezcla exitosa entre el refuerzo y la mitigación
- Charla de Google I/O: Protección de aplicaciones web con funciones modernas de la plataforma