Aikido

SvelteSpill: un error de engaño de caché en SvelteKit + Vercel

Escrito por
Jorian Woltjer

SvelteKit es un popular marco de trabajo JavaScript full-stack, y Vercel es su plataforma de implementación más común. ¿Qué pasaría si te dijéramos que todas las aplicaciones creadas con esta combinación son vulnerables a que los atacantes lean las respuestas de cualquier ruta de otros usuarios que hayan iniciado sesión?

Bueno, es cierto. Este vector de ataque, denominado «engaño de caché», es exactamente lo que uno de los agentes de IA encontró y nos informó mientras probábamos Aikido Attack. Aunque al principio nos mostramos escépticos, seguimos sus pasos y descubrimos que podíamos reproducir la vulnerabilidad a la perfección. Notificamos rápidamente a Vercel y la vulnerabilidad ya se ha solucionado automáticamente para todos los usuarios.

Nota: El incidencia puede consultarse en la base de datos Intel de Aikido y tiene el número CVE reservado: CVE-2026-27118. También hemos encontrado otro problema que permite un ataque de denegación de servicio en una función experimental de SvelteKit. Esto también se ha revelado y solucionado.

Resumen rápido

El __nombre_de_la_ruta El parámetro de consulta en el adaptador Vercel de SvelteKit puede anular la ruta desde cualquier lugar. En Vercel, todos los archivos bajo /_app/inmutable/ tiene diferentes Control de caché: encabezados para que pueda almacenarse en caché. Al anteponerle una ruta falsa y reescribirla para que contenga contenido confidencial, la respuesta se almacenará en caché de forma forzada, lo que permitirá al atacante recuperarla visitando la misma URL sin cookies.

URL de prueba de concepto:

https://example.vercel.app/_app/immutable/x?__pathname=/api/session

Descubrimiento

Contaremos esta historia desde la perspectiva del agente de pruebas de penetración de IA que encontró la vulnerabilidad, comenzando desde el gadget inicial hasta la explotación completa, y hablaremos de algunas ideas interesantes a lo largo del camino. ¡Acepta tu robot interior y comencemos a investigar!

Si lees el código fuente de SvelteKit, encontrarás algunos «adaptadores», que son el middleware entre una plataforma de alojamiento como Vercel y el marco SvelteKit. Esto permite aplicar algunas reglas especiales a la solicitud y respuesta requeridas para el funcionamiento interno de la plataforma. Vercel ha implementado lo siguiente en sin servidor.js:

const DATA_SUFFIX = '/__data.json';

fetch(request) {
    // If this is an ISR request, the requested pathname is encoded
    // as a search parameter, so we need to extract it
    const url = new URL(request.url);
    let pathname = url.searchParams.get('__pathname');

    if (pathname) {
        // Optional routes' pathname replacements look like `/foo/$1/bar` which means we could end up with an url like /foo//bar
        pathname = pathname.replace(/\/+/g, '/');

        url.pathname = pathname + (url.pathname.endsWith(DATA_SUFFIX) ? DATA_SUFFIX : '');
        url.searchParams.delete('__pathname');

        request = new Request(url, request);
    }

    return server.respond(request, {
        getClientAddress() {
            return /** @type {string} */ (request.headers.get('x-forwarded-for'));
        }
    });
}

Podemos leer «Si se trata de una solicitud ISR, la ruta solicitada se codifica como un parámetro de búsqueda». El código toma esta variable de ruta (de la ?__pathname= parámetro de consulta) y reescribe el url.ruta con él. La solicitud ignora la URL original y solo utiliza esta. __nombre_de_la_ruta en su lugar.

Pero no parece haber ningún control con respecto a estas solicitudes de regeneración estática incremental (ISR), así que, ¿todas las solicitudes admiten este parámetro?

La respuesta es sí, ¡y ahí es precisamente donde reside la vulnerabilidad! Cualquier ruta puede ser sustituida por cualquier otra ruta con solo un parámetro de consulta. Una prueba sencilla como /?__pathname=/404 Mostraría un error 404 en lugar de la página de inicio.

Esto puede parecer una característica extraña. ¿Por qué sería peligroso? Bueno, no lo es, hasta que entra en juego el almacenamiento en caché.

¿Envenenamiento de caché?

Si podemos reescribir cualquier ruta para que apunte a cualquier otro recurso, ¿qué ocurre si ese recurso contaminado se almacena en caché? Esa fue también nuestra primera idea. Si comprobamos el código fuente de cualquier aplicación SvelteKit, veremos algo como esto:


importar("./_app/immutable/entry/start.CLO1Dlt2.js"),	
import("./_app/immutable/entry/app.kQF6jJr8.js")

El camino /_app/inmutable/entrada/inicio.CLO1Dlt2.js devuelve algo de JavaScript para ejecutar, y al observar la respuesta, Control de caché: Los encabezados nos indican que efectivamente está almacenado en caché. Esto es lo esperado para los recursos estáticos:

Edad: 618
Control de caché: público, inmutable,edad máxima=31536000
X-Vercel-Cache: HIT

Si pudiéramos usar nuestro ?__pathname= parámetro para reescribir la solicitud y dirigirla a alguna ruta controlada por el atacante, como una carga de archivos, y seguiría almacenándose en caché con el mismo nombre sin cifrar. /_app/inmutable/entrada/inicio.CLO1Dlt2.js ruta que carga cada usuario, tendríamos XSS en cada usuario. Veamos qué sucede cuando añadimos este parámetro de consulta a nuestra solicitud:

GET /_app/immutable/start.CLO1Dlt2.js?__pathname=/ HTTP/2
Host: example.vercel.app

En la respuesta, recibimos buenas y malas noticias de los encabezados:

Edad: 935
Cache-Control: público, inmutable,max-age=31536000
X-Vercel-Cache: HIT

El Edad: ha aumentado en el tiempo que ha tardado en escribir el párrafo anterior, y el HIT significa que con nuestro parámetro de consulta añadido, se accede a la misma entrada de caché que la que carga todo el mundo. Pero, al mismo tiempo, eso significa que también se devuelve el contenido JavaScript antiguo, y no el contenido de nuestra ruta reescrita. Si ese es el caso, ¿podríamos vaciar la caché y ser los primeros en solicitarla para que nuestro ¿Se puede almacenar en caché la carga útil?

Por desgracia, no. La plataforma Vercel es más complicada debido a su arquitectura sin servidor, y en realidad ni siquiera llega al código del adaptador para un recurso tan estático. Esto se debe a que los activos estáticos se almacenan en caché y se devuelven en una capa superior, por lo que nunca podremos contaminarlos.

En este caso, Vercel es la única plataforma vulnerable, por lo que no podemos encontrar otros escenarios explotables. Nos encontramos en un callejón sin salida.

¡Engaño de caché!

Pasemos al siguiente problema relacionado con el almacenamiento en caché web: el engaño de caché. En esta técnica menos conocida, un atacante redirige a la víctima a una página confidencial a la que puede acceder con sus cookies. Si la caché almacena esta página sin tener en cuenta qué usuario la ha solicitado, el atacante puede visitar posteriormente la misma URL y ver los datos privados de la víctima desde la caché. El problema es que la caché guarda las respuestas basándose únicamente en la URL, ignorando que diferentes usuarios con diferentes cookies de inicio de sesión deberían ver contenidos diferentes.

Aplicando esta idea a nuestro ?__pathname= gadget, podemos crear nuestra URL parecer apunta a una ruta estática, pero en realidad apunta a contenido confidencial. La capa de almacenamiento en caché puede activarse en esa ruta de aspecto estático y añadir explícitamente Control de caché: encabezados y anular la respuesta privada.

Mientras que algunos criterios de caché están documentadas, nuestra investigación reveló más reglas. Una con la que ya estamos familiarizados: las rutas que comienzan con /_app/inmutable/. Resulta que no solo los archivos estáticos esperados se pueden almacenar en caché bajo este prefijo, sino que cualquier Respuesta 200 OK. Para evitar los activos ya generados, podemos apuntar inicialmente a un activo falso. A continuación, reescribirlo en cualquier ruta sensible, por ejemplo /api/sesión:

https://example.vercel.app/_app/immutable/x?__pathname=/api/session

Y ahí tenemos nuestra carga útil final. Al visitar este enlace como víctima conectada, se enviará una solicitud como la siguiente:

GET /_app/immutable/x?__pathname=/api/session HTTP/2
Host: example.vercel.app
Cookie: auth=...

El adaptador Vercel de SvelteKit reescribe la ruta de acceso a /api/sesión, y es gestionado por la aplicación. El auth= Se verifica la cookie y se devuelve su token de sesión:

HTTP/2 200 OK
Age: 0
Cache-Control: public, immutable, max-age=31536000
...
Server: Vercel
X-Vercel-Cache: MISS
Content-Length: 16

{"token":"1337"}

Mientras que el Control de caché: El encabezado normalmente diría edad máxima=0 Para este punto final, la capa de almacenamiento en caché de Vercel habilita el almacenamiento en caché de forma forzada debido a la /_app/inmutable/ prefijo.

Una vez que el atacante sabe que la víctima ha sido redirigida, puede solicitar la misma URL por sí mismo, sin necesidad de cookies:

GET /_app/immutable/x?__pathname=/api/session HTTP/2
Host: example.vercel.app

Reciben la misma respuesta que un HIT ahora, y puede leer el token de la víctima.

HTTP/2 200 OK
Age: 22
Cache-Control: public, immutable, max-age=31536000
...
Server: Vercel
X-Vercel-Cache: HIT
Content-Length: 16

{"token":"1337"}

Mediante el uso de caché busters (rutas ficticias únicas) y la comprobación tras la redirección, esto podría haberse explotado a gran escala. Dado que esta vulnerabilidad es solo parte de la base de Sveltekit, cualquier sitio web de SvelteKit en Vercel que utilice cookies para la autenticación permitiría recuperar respuestas arbitrarias.

Las secuelas

Rápidamente informamos del problema a Vercel, que propuso una solución devolver forzosamente un 404 en cualquier /_app/inmutable/ ruta y eliminar el __nombre_de_la_ruta parámetro. Dado que controlan toda su plataforma, esto resuelve el problema automáticamente para todos los usuarios, sin necesidad de aplicar ningún parche manual.

Una importante conclusión que se puede extraer de esta vulnerabilidad es que el almacenamiento en caché siempre es complicado. Reglas sencillas, como la coincidencia de prefijos, pueden ser aprovechadas por características inesperadas de la plataforma que ni siquiera has implementado.

Por eso las pruebas de penetración pueden ser tan útiles. Problemas como este serían difíciles de detectar solo con el código, pero mediante una implementación real se pueden encontrar y explotar reglas ocultas. La prueba de penetración de IA de Aikido puede detectar automáticamente ataques de envenenamiento y engaño de caché, como ha hecho con este problema.

Puntos clave

  • El 20 de enero de 2026, el sistema de pruebas de penetración con IA de Aikido detectó algo interesante en las aplicaciones SvelteKit implementadas en Vercel. 
  • Confirmamos que se trataba de una vulnerabilidad de engaño de caché que afectaba a las configuraciones predeterminadas. 
  • No se requiere ninguna configuración incorrecta. Solo SvelteKit + Vercel haciendo lo que se supone que deben hacer.
  • Impacto: Las respuestas autenticadas pueden almacenarse en caché y exponerse a otros usuarios.
  • Cualquier aplicación SvelteKit que se ejecute en Vercel con puntos finales protegidos puede verse afectada.

Cómo comprobar si te ves afectado

Uso del aikido:

Si eres usuario de Aikido, comprueba tu feed central. Puedes ver el problema en Aikido Intel aquí. Consejo: Aikido vuelve a escanear tus repositorios cada noche, aunque recomendamos activar también un escaneo completo.

Si aún no eres usuario de Aikido, crea una cuenta y conecta tus repositorios. Nuestra cobertura de malware propietaria está incluida en el plan gratuito (sin tarjeta).

La prueba de penetración de IA y el análisis de seguridad web de Aikido detectan automáticamente los flujos de engaño de caché y los comportamientos de reescritura inseguros.

Estado de la reparación

Se lo comunicamos a Vercel el 21 de enero de 2026.

Cronología

  • 20 de enero de 2026: Aikido Security la vulnerabilidad y creó un PoC funcional.
  • 21 de enero de 2026: Divulgación responsable a Vercel.
  • 23 de enero de 2026: Informe clasificado
  • 9 de febrero de 2026: Vercel confirma el informe y comienza a trabajar en una solución.
  • 19 de febrero de 2026: Vercel ha corregido la vulnerabilidad para todos los usuarios y se ha publicado un aviso (GHSA-9pq4-5hcf-288c). El problema se puede consultar en la base de datos Intel de Aikido y tiene el número CVE reservado: CVE-2026-27118.

También encontramos un problema que permite la denegación de servicio en una función experimental de SvelteKit. Esto también se ha revelado y solucionado.

Compartir:

https://www.aikido.dev/blog/sveltespill-cache-deception-sveltekit-vercel

Suscríbase para recibir noticias sobre amenazas.

Empieza hoy mismo, gratis.

Empieza gratis
Sin tarjeta

Asegura tu plataforma ahora

Protege tu código, la nube y el entorno de ejecución en un único sistema central.
Encuentra y corrije vulnerabilidades de forma rápida y automática.

No se requiere tarjeta de crédito | Resultados del escaneo en 32 segundos.