La autora del artículo, cuya traducción publicamos hoy, dice que React es su biblioteca favorita para crear interfaces interactivas. React es fácil de usar y está bastante bien protegido. Sin embargo, esto no significa que las aplicaciones React sean completamente invulnerables. Es muy fácil caer en una paz mental irracional, habiendo decidido que no puede preocuparse por los ataques XSS debido al hecho de que el proyecto usa React.
Las vulnerabilidades en React ocurren con mayor frecuencia cuando el desarrollador piensa que está utilizando los mecanismos de protección de esta biblioteca, aunque de hecho resulta que no es así. Por lo tanto, es importante evaluar correctamente las capacidades de React y saber qué tareas debe resolver un programador por sí mismo.

Hoy hablaremos sobre las vulnerabilidades típicas de React, cómo encontrarlas durante una revisión de código y cómo defenderse de ellas.
Primer ejemplo de scripts de sitios cruzados (muy breve)
Cross site scripting (XSS) es una vulnerabilidad del cliente que puede provocar problemas graves. Los ataques XSS ocurren cuando un atacante puede engañar a un sitio web y obligarlo a ejecutar código JavaScript arbitrario en los navegadores de sus usuarios.
El ataque XSS reflejado se lleva a cabo mediante un enlace que contiene información textual procesada por el navegador en forma de código. Por ejemplo, este es un campo de formulario en el que, en el lado del cliente, se ingresa un texto de solicitud especial.
Un ataque XSS almacenado es una situación en la que un atacante tiene acceso al servidor, y cuando el código ejecutado en el servidor genera lo que llega a la página web del cliente. Los vectores típicos de tales ataques están cargando comentarios e imágenes a los servidores.
El gusano
Samy explotó la vulnerabilidad MySSpace XSS. Fue uno de los virus de propagación más rápida de todos los tiempos.
Los sitios web vulnerables pueden exponer a sus usuarios al robo de contraseñas o datos personales. Y esta es la forma habitual de explotar otras vulnerabilidades. Los scripts maliciosos se usan con mayor frecuencia para enviar spam y redirigir a los usuarios a sitios fraudulentos. Esto puede dañar la reputación y el rendimiento de SEO de un sitio atacado con éxito.
Vulnerabilidad n. ° 1: control sobre el estado inicial de la página, que se utiliza durante la representación del servidor
A veces, cuando formamos el estado inicial de una página, nosotros, lo cual es peligroso, creamos un documento basado en una cadena JSON. Esta vulnerabilidad en el código se parece a esto:
<script>window.__STATE__ = ${JSON.stringify({ data })}</script>
Esto es peligroso porque el método
JSON.stringify()
, sin "pensar" en nada, convierte los datos que se le proporcionan en forma de cadena (siempre que sean datos JSON válidos), que es lo que se mostrará en la página. Si
{ data }
tiene campos que un usuario no confiable puede editar, como un nombre de usuario o información del usuario, algo como lo siguiente puede incrustarse en dichos campos:
{ username: "pwned", bio: "</script><script>alert('XSS Vulnerability!')</script>" }
Este patrón se usa a menudo en la representación del lado del servidor de las aplicaciones React que usan Redux. Estuvo presente en la documentación oficial de Redux, y como resultado, muchos tutoriales y plantillas de aplicaciones de muestra que se pueden encontrar en GitHub todavía lo usan.
No lo creo? Entonces ver por ti mismo. Busque en Google el texto "aplicación de ejemplo de representación del lado del servidor reaccionar" e intente este ataque en cualquiera de los resultados de búsqueda de la primera página.
▍ Identificación de vulnerabilidad durante la revisión de código
Busque llamadas al método
JSON.stringify()
que acepten variables que pueden contener datos no confiables en la etiqueta del
script
. Aquí hay un ejemplo que solía estar en la documentación de Redux:
function renderFullPage(html, preloadedState) { return ` <!doctype html> <html> <head> <title>Redux Universal Example</title> </head> <body> <div id="root">${html}</div> <script> window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState)} </script> <script src="/static/bundle.js"></script> </body> </html> ` }
Y aquí hay un fragmento de código de la aplicación de muestra que se encuentra en GitHub:
function htmlTemplate( reactDom, reduxState, helmetData ) { return ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ${ helmetData.title.toString( ) } ${ helmetData.meta.toString( ) } <title>React SSR</title> <link rel="stylesheet" type="text/css" href="./styles.css" /> </head> <body> <div id="app">${ reactDom }</div> <script> window.REDUX_DATA = ${ JSON.stringify( reduxState ) } </script> <script src="./app.bundle.js"></script> </body> </html> `; }
A veces, encontrar esta vulnerabilidad es un poco más difícil. El siguiente código también resultará inseguro, si no se realiza el escape correcto de
context.data
:
const RenderedApp = htmlData.replace('{{SSR}}', markup) .replace('<meta-head/>', headMarkup) .replace('{{data}}', new ArrayBuffer(JSON.stringify(context.data)).toString('base64'))
Al realizar la representación del lado del servidor, preste atención a lo que se representa exactamente. Si lo que ingresa el usuario no está debidamente protegido y se muestra en el DOM, esto puede ser peligroso.
▍ Protección
Una de las opciones para protegerse contra esta vulnerabilidad es usar el módulo
serialize-javascript
npm, que está diseñado para proteger el JSON de salida. Si está realizando la representación del servidor en un entorno que no es Node.js, deberá elegir el paquete apropiado para su idioma.
Aquí está el comando para instalar el módulo:
$ npm install --save serialize-javascript
Después de eso, debe importarlo en un archivo y reescribir el código anteriormente vulnerable que trata con la
window
siguiente manera:
<script>window.__STATE__ = ${ serialize( data, { isJSON: true } ) }</script>
Aquí hay un gran artículo sobre este tema.
Vulnerabilidad №2: enlaces insidiosos
La etiqueta
<a>
puede tener el atributo
href
, que contiene un enlace a otra página del sitio, a otro sitio, a algún lugar de la página actual. Los enlaces pueden contener scripts que se parecen a esto:
javascript: stuff()
. Si no conocía esta función HTML, pruébela ahora mismo copiando el siguiente código en la línea del navegador:
data:text/html, <a href="javascript: alert('hello from javascript!')" >click me</a>
Para los desarrolladores web, esto significa que si el contenido de los enlaces se establece en función de los datos ingresados por el usuario, el atacante puede agregar código malicioso que comience con
javascript:
a estos datos. Luego, si el usuario hace clic en un enlace incorrecto, se iniciará un script de atacante en el navegador.
Esta vulnerabilidad definitivamente no solo es característica de React, sino que es uno de los problemas que los desarrolladores de React a menudo encuentran cuando esperan que el valor correspondiente se escape automáticamente correctamente. Cabe señalar que en una versión
futura de React este problema será menos grave.
▍ Identificación de vulnerabilidad durante la revisión de código
¿Pueden los usuarios del proyecto agregar enlaces a páginas en las que otros usuarios pueden hacer clic? Si es así, intente agregar un "enlace" a la página como el siguiente:
javascript: alert("You are vulnerable to XSS!")
Si se muestra el cuadro de mensaje correspondiente haciendo clic en el enlace, esto significa que el proyecto es vulnerable a los ataques XSS. Prueba esto donde sea que puedas agregar enlaces. Es probable que no todos esos lugares sean vulnerables.
▍ Protección
La protección contra esta vulnerabilidad no solo es adecuada para los proyectos React. Lo que debe hacerse exactamente depende de la aplicación. Además, es posible que deba hacer correcciones en el servidor.
Puede pensar que para resolver el problema, es suficiente eliminar el prefijo
javascript:
de los datos. Este es un ejemplo del uso de la estrategia de la lista negra, que no puede considerarse exitosa en la
limpieza de datos . Los piratas informáticos tienen formas ingeniosas de eludir dichos filtros, por lo que, en lugar de tal movimiento (o además de él), haga que los enlaces usen un protocolo incluido en la lista blanca (por ejemplo,
http:
y escapen de las entidades HTML.
Aquí hay un artículo detallado sobre este tema con respecto al blindaje de propiedades que un atacante puede controlar.
Otra estrategia que puede agregar un nivel adicional de protección al proyecto es utilizar el mecanismo para abrir enlaces personalizados en nuevas pestañas del navegador. Sin embargo, no recomendaría usar esta estrategia como la única "línea de defensa" del proyecto. Abrir
javascript:
enlaces en una nueva pestaña es un ejemplo del comportamiento no estándar de los elementos de la página. La mayoría de los navegadores ejecutarán el script en una pestaña vacía sin dañar al usuario, pero esto no está garantizado, y probablemente pueda evitarlo, lo que depende del navegador.
Considere usar el componente especial
UserLink , lo que conducirá al hecho de que es menos probable que la etiqueta vulnerable
<a>
llegue a las páginas de su proyecto en el futuro. Además, vale la pena agregar algunas pruebas y reglas de alineación al proyecto, con el objetivo de identificar código potencialmente peligroso y evitar que entre en producción.
Los enlaces no son las únicas entidades que se pueden usar de esta manera. Pero son el objetivo de ataque más probable en las aplicaciones React. Cualquier elemento puede ser vulnerable a este ataque si el atacante puede controlar su valor de
URI
. Otra posibilidad de llevar a cabo este ataque, por ejemplo, es un diseño de vista. Se puede encontrar una lista completa de atributos que pueden contener URI en
esta lista usando la palabra clave
%URI
usando la búsqueda del navegador (
Ctrl+F
).
Vulnerabilidad # 3: malinterpretar el significado de la construcción peligrosamente
Estoy extremadamente agradecido a React de que la advertencia de seguridad está directamente en el nombre del método. Este es el nombre
dangerouslySetInnerHTML
. A pesar de esta advertencia, todavía nos enfrentamos a menudo con el hecho de que los desarrolladores toman riesgos al realizar operaciones inseguras. Lo mismo puede decirse de
eval()
.
Considere el siguiente ejemplo que encontré en el sitio desde la primera página de resultados de búsqueda de Google:
<script dangerouslySetInnerHTML={{ __html: `window.__PRELOADED_STATE__ = ${JSON.stringify(initialState)};`}}></script>
Este es un ejemplo de vulnerabilidad No. 1, pero con una característica que debería llamar inmediatamente la atención sobre el hecho de que algo está mal aquí. Cuando encontré esto, se hizo un intento de explicar: "Usamos
peligrosamente SetInnerHTML como un método para limpiar datos y prevenir ataques XSS". Pues no! Esto esta mal. No lo hagas. Para obtener más información acerca de
dangerouslySetInnerHTML
, lea la documentación de React.
Otro ejemplo de que esto realmente sucede todo el tiempo es cómo los miembros de un equipo descubrieron que tenían una
vulnerabilidad cuando agregaron el marcado de Markdown a una página usando
dangerouslySetInnerHTML
. Para protegerse de esto en el futuro, comenzaron a usar una regla especial de linting.
▍ Identificación de vulnerabilidad durante la revisión de código
Antes de enviar solicitudes de extracción o realizar operaciones de fusión, es útil buscar en el código
dangerouslySetInnerHTML
cadenas
dangerouslySetInnerHTML
eval
y
eval
(también busco los comandos de
console.log
esta manera) o usar la regla de interfaz correspondiente.
▍ Protección
Asegúrese de que en todos los casos de usar el método
dangerouslySetInnerHTML
, solo los datos en los que puede confiar se carguen en la página. ¿Cómo saber si se puede confiar en los datos? Si algo no viene de ti, puede ser una amenaza. Esto incluye los datos descargados de API externas y lo que se emite utilizando las herramientas de Markdown.
Nota de suplantación de componentes
En 2015,
alguien descubrió que puede falsificar componentes pasando JSON a aquellos componentes que esperan texto. Pude encontrar solo un caso de un mensaje de falsificación de componentes y una
larga discusión causada por este mensaje. La discusión se centró en lo que React es responsable de prevenir los ataques XSS. Como resultado, los desarrolladores de React lanzaron una
solución que parece haber ayudado a solucionar esta vulnerabilidad.
Decidí no incluir una historia sobre esta vulnerabilidad en el artículo, pero este tema puede ser de interés para futuras investigaciones.
Nota de SSR
La vulnerabilidad de representación del servidor está tan extendida debido al hecho de que estaba presente en la documentación de Redux y, como resultado, se extendió a muchos otros materiales. Este problema se solucionó en 2016. Pero incluso hoy, después de tres años, las guías para principiantes diseminadas por todo el Internet todavía enseñan lo que no debes enseñar.
Por cierto, aquí está tu tarea: encuentra un ejemplo de este problema en GitHub y envía una solicitud de extracción para solucionarlo.
Aquí hay un ejemplo .
¡Juntos podemos deshacernos de esta vulnerabilidad de una vez por todas!
Estimados lectores! ¿Has encontrado ataques en tus proyectos React?
