Se acabó para NextJS... 13 NUEVAS vulnerabilidades

BBetter Stack
Computing/SoftwareBusiness NewsInternet Technology

Transcript

00:00:00Sucedió de nuevo. Este es como mi tercer video sobre CVEs de Server Components este año y ni
00:00:05siquiera creo haberlos cubierto todos. Esta vez son 13 CVEs, sí 13, en React
00:00:11y Next.js, 6 de los cuales son de alta gravedad e incluyen denegación de servicio, bypasses
00:00:15de middleware, cross-site scripting y más. Quizás los Server Components fueron un error.
00:00:20Aquí está el lanzamiento de seguridad de Next.js, ya saben, arreglando unos cuantos problemas casuales
00:00:28que han tenido este mes y abajo, obviamente, la resolución es actualizar
00:00:32todas sus versiones de Next.js; estas son las versiones afectadas. Vale la pena notar
00:00:36que TanStack no está afectado por esto, lo cual, quizás sea parcial, pero es otra razón para usar
00:00:41TanStack para mí. No repasaré todos estos porque probablemente estaríamos aquí un buen rato
00:00:44y tampoco he encontrado exploits que funcionen para todos, pero quiero mostrarles uno
00:00:48de cada categoría y empezaremos con un bypass de middleware y proxy; el que
00:00:52logré recrear es este del Pages Router. Tenemos un bypass de proxy de middleware en el
00:00:56Pages Router si usas i18n y puedes ver que este es un CVE con una gravedad de
00:01:027.5 sobre 10. Este es un ejemplo de una aplicación vulnerable; en la configuración de Next.js he
00:01:06activado i18n y también configurado dos locales: inglés y francés; luego tengo un archivo de middleware
00:01:12que en versiones posteriores de Next.js se renombró a proxy para intentar evitar
00:01:16la confusión que estoy por mostrarles, pero esencialmente lo que el middleware debería permitirnos
00:01:19hacer es modificar una petición entrante, ya sea redirigiéndola, reescribiéndola o añadiendo
00:01:24algunos encabezados o algo así. En mi caso, lo uso para que si intentas visitar la
00:01:28página /secret, verifique si tienen una cookie de sesión para saber si están logueados y si
00:01:32no, debería redirigirlos a la página de login, así que ojalá solo usuarios autorizados vean
00:01:37mi página secreta. Al final también tenemos un “matcher” para que cualquier middleware que
00:01:41apliquemos a esa página secreta también coincida con las variantes de locale porque técnicamente,
00:01:45ahora que tenemos dos locales, tenemos tres versiones de esta URL. En la página secreta en sí,
00:01:50luego también tengo algunos server-side props; estos deberían obtenerse del servidor al renderizar
00:01:54y de nuevo, como configuramos ese middleware, teóricamente solo un usuario logueado debería ver
00:01:58cuáles son estos valores que luego uso en la página misma: un email, un flag y
00:02:03también un encabezado. De nuevo, solo un usuario autorizado debería ver esto. Pongámoslo
00:02:07a prueba entonces; lo primero que intentaré es acceder a esa página secreta y ven
00:02:11que soy redirigido al login porque no estoy logueado, lo que significa que el middleware funciona,
00:02:16pero ¿y si nos volvemos maestros hackers? Bueno, podríamos hacerlo primero inspeccionando elemento,
00:02:20cosas de hackers absolutamente locas, y luego en el script de next data aquí abajo debemos buscar
00:02:24nuestro build ID; en mi caso es este de aquí y podemos proceder a copiarlo.
00:02:28Luego debemos escribir una URL que es underscore next slash data slash el build ID que
00:02:32acabamos de copiar y luego la página a la que intentamos acceder punto JSON. Una vez hecho eso,
00:02:37pueden ver que recuperamos esos props que deberían haber estado tras ese middleware y protegidos,
00:02:40que en mi caso eran un flag, email, encabezado y también algo diciéndote que te suscribas
00:02:44para más noticias de desarrollo, tutoriales y trucos. Adelante, hazlo, espero haberte
00:02:48impresionado con mis locas habilidades de hacker, pero ¿por qué sucede esto en realidad? Bueno,
00:02:52esto es tan fácil de explicar como lo fue hacerlo; teníamos nuestra página secreta y server side props,
00:02:56en Next.js los server side props se sirven desde una URL que se ve así, pero nuestro middleware
00:03:00debería haber estado protegiendo esta ruta. El problema es que al usar i18n también
00:03:05teníamos otras dos URLs que eran la variante en inglés y francés; pueden ver que los
00:03:09server side props también obtienen las variantes en inglés y francés, y Next.js simplemente tenía código
00:03:13defectuoso que hacía que, si i18n estaba activado, no protegía el caso base; así que
00:03:18este no estaba incluido en el matcher, pero los otros dos sí; las versiones en inglés y francés estaban
00:03:22protegidas, pero no el caso base de slash secret. Podemos verlo rápidamente si cambio
00:03:26esta URL a la versión en inglés: sí soy redirigido a la página de login. Una vulnerabilidad increíblemente simple,
00:03:31pero seré honesto, estos bypasses de middleware suelen sonar mucho peor de lo que
00:03:35son; no son geniales, pero realmente no deberías proteger mucho solo con middleware de todos modos,
00:03:40y Next.js ni siquiera recomienda que lo hagas. Si tuvieras datos sensibles
00:03:44en esos server side props y no tuvieras ninguna lógica de auth en el servidor, bueno, siento que
00:03:48parte del problema es tuyo también; así que pasemos a uno más dañino: denegación de
00:03:53servicio. Hubo tres de estos, pero solo hubo uno que pude recrear de forma fiable,
00:03:56y fue este de aquí: denegación de servicio con Server Components; esto afecta a Next.js
00:04:01así como a cualquier cosa que use el paquete react-server-dom, que es básicamente solo Next.js
00:04:05y los otros frameworks que lo han copiado como Vinxt y algunos otros forks. TanStack
00:04:10Start no usa esto, así que no es vulnerable. Pueden ver que esto también tiene una gravedad
00:04:14de 7.5 sobre 10. Con este, todo lo que necesitas es una aplicación Next.js muy simple y,
00:04:18en ella, necesitas estar usando una Server Action, pero de nuevo, puede ser una muy simple. Este es
00:04:22el sitio funcionando y pueden ver que cuando refresco la página casi no hay
00:04:25carga, es casi instantáneo; para ponerle números reales, si envío esta petición,
00:04:29verán que se resuelve en 0.02 segundos, pero si ahora ejecuto mi exploit y luego
00:04:34envío esa petición de nuevo, esta vez tardó seis segundos y eso fue ejecutando el exploit
00:04:39una sola vez; imaginen qué pasaría si lo encadenara. Ahora, para entender este exploit debemos
00:04:42saber un poco sobre el protocolo React Flight, que es el formato que React usa
00:04:46para serializar árboles de componentes y datos entre el servidor y el cliente. Probablemente
00:04:50ya hayan visto esto antes; en esta página teníamos un formulario con una Server Action. Si voy
00:04:54a la pestaña de Red aquí y hago clic en enviar, verán que el payload realmente se
00:04:58envía como datos que se ven así, que parece algo sin sentido, y lo mismo para la
00:05:02respuesta aquí. Si copiamos este payload puedo explicar qué sucede cuando se
00:05:05envía al servidor. El primer paso es la deserialización e iniciará en el chunk 0 donde tenemos
00:05:10este $k1. Este $k1 es solo un puntero diciendo que habrá algunos datos de formulario aquí
00:05:16que comienzan con un guion bajo; así que tomará todas las otras claves que enviamos
00:05:20como parte de ese payload, pasará por todas ellas buscando un string que comience con
00:05:24un guion bajo y sabrá que esta será la clave y que este es el valor. Una vez
00:05:28hecho eso, podrá decir que tenemos nombre, email, mensaje y simplemente convertirá estos datos
00:05:32en este objeto que tenemos aquí abajo. Rápido y sencillo. El problema con este enfoque
00:05:36es qué sucede cuando lo escalamos. Digamos que añado otro puntero, esta vez buscando $k2,
00:05:41esto va a buscar todas las claves que comiencen con dos guiones bajos. El problema
00:05:44es que ahora, estando en $k1, va a pasar por estas seis buscando las
00:05:48que comiencen con un guion bajo y cuando pase a $k2 hará exactamente lo
00:05:52mismo pero buscando las que inicien con dos guiones bajos. Así que ahora pasamos por 12
00:05:56claves en total. Eso no está tan mal, pero llevémoslo al extremo. Si añadimos 199,999
00:06:03claves aleatorias al payload que estamos enviando y luego cambiamos nuestro array en el cero aquí
00:06:07para ir de $k1, $k2 hasta $k1000, significa que tendrá que buscar un guion bajo,
00:06:12dos guiones bajos, tres guiones bajos hasta mil guiones bajos por todas nuestras 200,000
00:06:17claves que tenemos en nuestro payload, y eso significa que en total hará 200 millones de
00:06:21comparaciones de strings. Como imaginarán, eso bloqueará el hilo por varios segundos.
00:06:25Este es el commit que creo solucionó el problema que teníamos. Pueden ver que
00:06:28pasan muchas cosas en este commit y, honestamente, es algo complejo, pero intentaré
00:06:33explicarlo lo mejor que pueda. Esencialmente, ahora usan un sistema basado en cursores para las claves,
00:06:36así que cargan todas estas 200,000 claves de nuestro payload en una lista y luego
00:06:41empezamos en el cero aquí buscando la referencia $k1 y comienza a bajar por
00:06:45esa lista con un cursor que no puede retroceder. Baja por aquí hasta $j1, ve que no
00:06:50coincide con el guion bajo necesario, pasa a $j2, tampoco coincide con un guion bajo
00:06:54y continúa por toda la lista de claves hasta $j199,999. Una vez que llega
00:07:01aquí, se da cuenta de que no hay coincidencia para $k1 y pasa a $k2. Ahora $k2 busca
00:07:06dos guiones bajos, pero el problema es que, al ser un sistema de cursor y este cursor
00:07:09no puede retroceder, se queda inmediatamente al final de la lista, así que eso también
00:07:14será indefinido y continuará así hasta $k1000. Así que esta vez solo hemos
00:07:18recorrido 200,000 claves. Básicamente, este fix ha reducido el número de operaciones de
00:07:23$k*n, donde $k es el número de referencias $k y $n es el número de claves, a
00:07:27solo $n+k. En nuestro caso pasamos de 200,000,000 de operaciones a 201,000, ya que
00:07:33todavía debe recorrer todas las claves y también esas referencias $k. Creo que este
00:07:37tweet de Prime resume muy bien la situación. Crear tu propio protocolo con serialización
00:07:41es increíblemente difícil, así que no sorprende que veamos tantos problemas. En mi opinión,
00:07:46deberían pedirle a Claude Mythos que le dé una revisada al código de React y Next.js. A
00:07:50continuación tenemos el CV de mayor gravedad de todos: Server-Side Request Forgery en
00:07:54Como pueden ver, esta tiene una puntuación de 8.6 sobre 10, pero también cabe
00:07:59notar que no afectó a implementaciones en Vercel, solo a las auto-alojadas u otros proveedores.
00:08:04Este exploit es también super simple de aprovechar. Primero lanzamos nuestro servidor Next.js
00:08:09y, de nuevo, puede ser una aplicación Next.js de base, sin modificaciones.
00:08:14Luego también querremos un servidor interno. Digamos que este servidor solo es accesible
00:08:18por el servidor Next.js y no por el mundo exterior, por ejemplo, en nuestro despliegue en la nube.
00:08:23Entonces lo que haremos es enviar una petición curl muy simple donde dirigimos
00:08:26el curl a nuestra aplicación Next.js en el puerto 3002 y decimos que nuestro
00:08:31“request target” sea el servidor al que queremos acceder en esa URL de localhost. Si ahora
00:08:36presiono enter, pueden ver lo que devuelve: es en realidad la página HTML de un servidor Python
00:08:40donde muestra el listado básico de directorios, y si vuelvo al servidor Python mismo,
00:08:45verán que tuvo una petición entrante que en realidad vino de la aplicación Next.js.
00:08:49Para visualizarlo mejor, digamos que en nuestra línea punteada está nuestro despliegue de Next.js,
00:08:53donde tenemos nuestro servidor Next.js y también algunos servicios internos, ya sea Redis,
00:08:57una base de datos o cualquier otro servicio, y este de aquí no quieres que tenga acceso público,
00:09:02así que nadie puede enviarle un curl; simplemente fallará, está tras un firewall y bloqueado,
00:09:06solo puede ser accedido por el servidor Next.js. Lo que hemos hecho es simplemente enviar
00:09:10un curl al servidor Next.js y decirle: “Oye, ¿puedes enviar una petición por nosotros
00:09:15al servicio interno?”, y obtuvimos la información de vuelta, así que saltamos el firewall
00:09:19pasando por Next.js, que sí tiene acceso al servicio interno. La causa raíz de
00:09:23esto es bastante simple: básicamente en nuestro curl enviamos un encabezado de
00:09:28“upgrade websocket” y cuando enviamos estos encabezados lo que sucede en Next.js es que llegamos a esta
00:09:32parte del código, y esto resuelve las rutas en nuestra URL, pero la URL procesada que obtenemos
00:09:37es en realidad el request target que enviamos en nuestro curl, así que este va a ser el
00:09:40objetivo al que intentamos llegar en ese servidor interno y no la aplicación Next.js en sí.
00:09:45Lo que pasa con esta URL procesada es que pasa por una verificación abajo para ver si
00:09:49tiene un protocolo, y en este caso sí, porque usamos HTTP y eso es un protocolo,
00:09:55así que procede a hacer el proxy de esa petición por nosotros. El fix para esto añade dos
00:09:58nuevas guardas en nuestra función resolve routes; ahora obtenemos un booleano de
00:10:02finalizado así como un código de estado. Lo que esta función realmente hace es
00:10:06tomar nuestra URL y procesar si es una petición de proxy legítima basada en nuestros
00:10:11rewrites, middleware y demás de Next.js; si no lo es, significa que “finished” será
00:10:15falso, así que abajo tenemos una comprobación: si es verdadero pasamos al siguiente paso, pero si
00:10:20no, no se ejecutará, lo que significa que no se hará la petición de proxy.
00:10:24En el caso de nuestra petición curl anterior, eso es exactamente lo que va a
00:10:27suceder, “finished” será falso. Ahora, si por alguna razón fuera verdadero, nuestra
00:10:32siguiente verificación es el código de estado; cuando resolve routes corre obtenemos un código si la
00:10:36petición es HTTP, será 200, 404 o lo que sea; así que si hay un código
00:10:41significa que no es una petición de proxy websocket válida, por lo que ignorará
00:10:45esta línea y no la ejecutará. He intentado profundizar en estos temas en este video,
00:10:49así que dime si sigues viendo comentando algo aleatorio, no sé, como
00:10:53“bar” o algo así, y suscríbete si aprecias el contenido. Nos quedan dos
00:10:58categorías más, pero estas deberían ser más rápidas, y empezaré
00:11:01con envenenamiento de caché, donde logré recrear este problema moderado de aquí.
00:11:04Es un envenenamiento de caché en React Server Component y tiene un ranking de 5.4 de 10. Para recrear
00:11:09esto tengo una aplicación Next.js pero también un CDN falso para simular que esto está
00:11:14en un entorno real desplegado. Esto significa que si visito mi sitio por primera vez en
00:11:18su URL de CDN, hago clic en ver productos, vuelvo y hago clic de nuevo, la primera vez
00:11:23debería ser un “cache miss” y la siguiente un “cache hit”. Podemos verlo en los logs
00:11:27aquí: primero tuvimos un miss en slash productos con un query string y las siguientes fueron un
00:11:31hit. Lo que hice luego fue limpiar la caché para simular que tal vez expiró
00:11:35o algo más, y ahora puedo enviar esta petición curl. De vuelta en mi aplicación,
00:11:39si hago clic en ver productos ahora, verán que recibimos un montón de basura; hemos
00:11:44hecho con éxito un ataque de envenenamiento de caché. Lo que creo que sucede es que cuando enviamos el
00:11:47curl con el encabezado de react-server-components en uno, esta URL devolverá sus datos de
00:11:52server component en lugar de HTML. Luego, cuando esto debe cachearse, Next pasará
00:11:58por una función que verifica si son datos de server component o no. Si lo son,
00:12:02los guardará en caché como tales, y si no, los guardará como HTML. Esto significa que,
00:12:07teóricamente, cuando el usuario intente obtener la versión HTML de la página, por ejemplo
00:12:11haciendo clic en el botón, nunca debería recibir datos de server component. El problema es que en
00:12:16nuestro caso, al tener ese query string al final de nuestra petición curl de server component,
00:12:20no cumplía con los requisitos para verificar si era un server component o no, ya que
00:12:24solo revisa si termina en .rsc, pero nuestra versión termina en el query string, así que
00:12:29ahora cree que es HTML y lo guarda en caché como tal; la próxima vez que un usuario hace
00:12:33clic en el botón, recibe los datos de server component porque el sistema creyó que
00:12:37era HTML. La solución para esto fue increíblemente simple: al verificar si
00:12:41termina en .rsc, ahora simplemente ignoran los query strings. Pasando ahora a nuestra
00:12:46categoría final de CVEs, tenemos algunos de cross site scripting; este es el que logré
00:12:50recrear: tiene un 6.1 de 10 y es un cross site scripting en scripts de tipo
00:12:55“before interactive” con entrada no confiable. Básicamente lo que esto significa es que en Next.js, si tengo
00:12:59una etiqueta script con estrategia de “before interactive”, y luego tengo otro atributo en ella que
00:13:03necesita contenido no confiable, por ejemplo, este viene de los parámetros de búsqueda,
00:13:08puedo hacer cross site scripting. Puedo hacerlo logrando que el usuario haga clic en un enlace
00:13:12como este, por ejemplo, donde he incrustado mucho contenido en ese parámetro de búsqueda; si alguien
00:13:16hiciera clic en ese link, esto es lo que vería; podrían pensar que tienen que
00:13:19loguearse de nuevo en el sitio, y cuando hacen clic en “sign in”, verán aquí que era un
00:13:22formulario de login totalmente falso inyectado vía ese parámetro; esencialmente permite al atacante
00:13:26ejecutar JavaScript en la máquina de la víctima; creo que un ejemplo más realista
00:13:31sería robar las cookies de sesión de Chrome para entrar en todo lo que tuvieras acceso.
00:13:34Este es un ejemplo simple de un escape incorrecto y podemos verlo más fácil con
00:13:39esta versión simplificada; todo lo que hago en ese parámetro es cerrar primero una
00:13:43etiqueta script y luego abrir una nueva con lo que yo quiera ejecutar; cuando presionamos
00:13:47enter, ven que sale la alerta que dice “pwned”. Como les mostré con mi app,
00:13:51la forma en que esto funciona es que necesitamos una etiqueta script de Next.js con estrategia “before interactive”
00:13:55y algún atributo en ella que tome datos de una fuente no
00:13:59confiable, en mi caso de los parámetros de consulta; lo que esto hace en Next.js es que esta
00:14:04etiqueta script se convierte en algo como esto, donde tenemos un “dangerouslySetInnerHTML”.
00:14:08El nombre ya indica que será peligroso, y también tenemos un JSON.stringify.
00:14:12Lo importante de JSON.stringify es que no escapa caracteres HTML
00:14:17como los corchetes de cierre; así que lo que realmente sucede aquí es que tomamos todos los scripts
00:14:21configurados en Next.js, busca la fuente que se establecerá aquí y
00:14:24luego el resto de los props contendrá el ID de seguimiento así como el valor que
00:14:29pusimos en nuestro parámetro de consulta, y eso entra en ese JSON stringify. Bueno, esto
00:14:33se renderiza en la página como algo así: tenemos nuestro ID de seguimiento,
00:14:37que era el resto de los props, pero también el string que insertamos vía parámetros
00:14:41de consulta; si expandimos cómo se ve realmente en la página, es algo como
00:14:45esto: tenemos nuestro script, el ID de seguimiento, pero justo después en realidad
00:14:49hay una etiqueta de cierre de script; así que termina el script que Next.js intentaba hacer y
00:14:53tras esto podemos ejecutar el script que queramos en la página; también tenemos este trozo extra
00:14:58al final porque, si no estuviera, los analytics se renderizarían en la
00:15:01página como texto ya que el HTML vería esto como texto; esto simplemente lo
00:15:05absorbe para que no haya señales claras de que algo malo pasa en la página. Y ahí lo tienen,
00:15:09esos son 13 CVEs para Next.js y React esta semana y cómo funcionaron algunos. Sinceramente,
00:15:14no sé qué pensar sobre esto; lo odio, y lo digo desde un lugar donde hace un
00:15:18par de años cada proyecto que hacía era en Next.js y pensaba que era lo mejor, el
00:15:22futuro; pero parece ser obstáculo tras obstáculo, da la sensación de que se han precipitado
00:15:26en algunas cosas y luego han tenido que arreglarlas. Personalmente, ahora estoy totalmente volcado con TanStack y
00:15:31también Astro cuando necesito un sitio basado en contenido; me parecen mucho más simples y,
00:15:35siendo honesto, también me gusta mucho lo que Cloudflare ha estado haciendo últimamente, así que estoy migrando
00:15:39mis proyectos allí; aunque todavía tengo unos 20 en Vercel y necesito ir
00:15:43a actualizarlos. ¿Qué piensan ustedes? ¿Serán útiles algún día los Server Components o hemos intentado y fallado?
00:15:48Díganmelo en los comentarios abajo, suscríbanse y, como siempre, nos vemos en
00:15:51el próximo.

Key Takeaway

La actualización inmediata de Next.js es obligatoria para mitigar 13 fallos de seguridad críticos, que incluyen fugas de datos por SSRF y bloqueos de servidor por ineficiencias en la deserialización del protocolo React Flight.

Highlights

  • Next.js y React presentan 13 nuevas vulnerabilidades CVE, incluyendo 6 de alta gravedad con puntuaciones de hasta 8.6 sobre 10.

  • El fallo de Server-Side Request Forgery (SSRF) permite saltar firewalls y acceder a servicios internos como bases de datos o Redis mediante encabezados de upgrade websocket.

  • Un ataque de denegación de servicio (DoS) en React Server Components puede bloquear el hilo del servidor durante 6 segundos mediante el envío de 200,000 claves en el payload.

  • Las aplicaciones con i18n activado en el Pages Router sufren un bypass de middleware que expone props sensibles del servidor a través de la ruta _next/data.

  • La vulnerabilidad de envenenamiento de caché ocurre porque el sistema ignora los query strings al validar la extensión .rsc, almacenando datos de componentes como si fueran HTML.

  • El uso de JSON.stringify en scripts con estrategia before interactive permite inyectar etiquetas de cierre y ejecutar JavaScript malicioso (XSS) en el navegador del usuario.

Timeline

Panorama de seguridad en React y Next.js

  • El ecosistema de React y Next.js registra 13 nuevas vulnerabilidades CVE en un solo mes.
  • Seis de estos fallos poseen una calificación de alta gravedad debido a riesgos de DoS y bypass de seguridad.
  • La resolución definitiva para todos los problemas expuestos es la actualización a las versiones más recientes del framework.

El lanzamiento de seguridad detalla múltiples vectores de ataque que afectan tanto al App Router como al Pages Router. Las vulnerabilidades abarcan desde cross-site scripting hasta bypass de proxies. Los frameworks alternativos como TanStack no se ven afectados por estos errores de implementación específicos.

Bypass de Middleware y Proxy en el Pages Router

  • El uso de i18n en el Pages Router deja desprotegida la ruta base de las páginas secretas.
  • Los atacantes extraen datos sensibles del servidor solicitando el archivo JSON asociado al build ID de la aplicación.
  • El error reside en código defectuoso que excluye la ruta principal del matcher de seguridad cuando existen múltiples locales configurados.

Al inspeccionar el build ID en el código fuente, un usuario no autorizado puede construir una URL bajo el prefijo _next/data para obtener los props de una página protegida. Aunque el middleware bloquea el acceso a las versiones traducidas (como /en/secret), permite la descarga de datos de la ruta base (/secret). Esto expone información privada como correos electrónicos o tokens de sesión si la lógica de autorización depende exclusivamente del middleware.

Denegación de servicio en React Server Components

  • El protocolo React Flight presenta una vulnerabilidad de complejidad algorítmica en la deserialización de datos.
  • Un payload diseñado con 200,000 claves y punteros múltiples eleva el tiempo de respuesta de 0.02 a 6 segundos.
  • La solución técnica implementa un sistema de cursores que reduce las operaciones de una escala multiplicativa a una aditiva.

El servidor buscaba coincidencias de claves de forma iterativa, realizando hasta 200 millones de comparaciones de strings por cada petición maliciosa. Este proceso bloquea el hilo de ejecución, impidiendo que el servidor atienda otras solicitudes. El parche de seguridad introduce una lista de claves con un cursor unidireccional que evita que el motor de React repita comparaciones ya realizadas.

Fuga de datos mediante Server-Side Request Forgery

  • Una vulnerabilidad de nivel 8.6 permite usar el servidor de Next.js como proxy para atacar redes internas.
  • El fallo afecta principalmente a infraestructuras auto-alojadas fuera de la plataforma Vercel.
  • La inserción de un request target externo en una petición curl redirige el tráfico hacia servicios ocultos tras el firewall.

Al enviar un encabezado de upgrade websocket, Next.js procesa la URL del objetivo sin validar si pertenece a la aplicación legítima. Esto permite que un atacante externo solicite datos de servicios internos como Redis o bases de datos locales que normalmente no tienen acceso público. El fix añade guardas en la función resolve routes para verificar si la petición de proxy es legítima según las reglas de reescritura configuradas.

Envenenamiento de caché y Cross-Site Scripting

  • El envenenamiento de caché ocurre cuando datos de Server Components se almacenan erróneamente como HTML debido a query strings.
  • La falta de escape de caracteres en etiquetas script antes de la interacción facilita ataques de XSS.
  • JSON.stringify no neutraliza etiquetas de cierre de script, permitiendo la inyección de formularios falsos o robo de cookies.

En el caso de la caché, añadir un parámetro de búsqueda al final de una petición .rsc engaña al sistema de validación, sirviendo datos basura a usuarios legítimos. Por otro lado, el fallo de XSS explota el atributo dangerouslySetInnerHTML en scripts interactivos. Un atacante puede cerrar la etiqueta original y abrir una nueva para ejecutar código arbitrario, como inyectar un formulario de inicio de sesión falso para capturar credenciales de los usuarios.

Community Posts

View all posts