reactjs / reactjs/react.dev

[Typo]: Suspense example code promoting data leakage when used in Next.js under app router

Aberta
#8,134 5 comentários 0 reações 0 responsáveis Ver no GitHub

Ninguém assumiu esta issue ainda.

type: typos
Linguagem predominante
JavaScript
Estrelas
11.8k
Forks
7.9k
Merge médio
1d 11h
PRs com merge (30d)
11

Descrição

Summary

The docs on React Suspense include examples that lead to data leakage when used in Next.js. The issue is that the cache map is constructed globally, which will remain across user sessions. Therefore, if any api fetch with user credentials happens, the returned data will leak to other users. This issue occurs both on app router's server and client components. I haven't tested the pages router yet.

Idk if Typo is the right type of issue. Feel free to tag it differently.

Page

https://react.dev/reference/react/Suspense

Details

In your documentation of React Suspense, you clearly state, users should use it in frameworks like Next.js:

Data fetching with Suspense-enabled frameworks like Relay and Next.js

Your example however fetches data like this:

let cache = new Map();

export function fetchData(url) {
  if (!cache.has(url)) {
    cache.set(url, getData(url));
  }
  return cache.get(url);
}

Which is then used like this:

const albums = use(fetchData(`/${artistId}/albums`));

I only tested this on the app router, but the pages router likely has similar issues. When this code is run server side, it will put the fetched data inside the cache and return the cached data in any new request. So, if I add authentication to this API fetch, we will get data leakage. The first user requests the page, data will be cached, and all subsequent fetches will expose the user data.

This also happens if "use client" is used in the app router. Because next will render static html and then hydrate it with React javascript.

In that sense, your example also kind of promotes fetching the api twice under the app router. Because both the server and the client will both fetch and suspend due to the api request.

Steps to reproduce

  • Create a next.js app with app router
    pnpm create next-app@latest my-app --yes
    cd my-app
    pnpm dev
    
  • Copy the example code into a component such as ArtistPage.js, Albums.js, data.js
  • Add a console.log to the getData function.
What happens:

If used blank, ArtistPage and Albums are treated as server components. The client won't fetch data and therefore doesn't console.log. The server will print once and then never again, since it has the data already.

If used under "use client" (add that to the top of ArtistPage.js and Albums.js), the server logging behaviour will stay the same, but the client will also log. (This shows the double fetching behaviour as well)

Why I think this is a big issue

Users looking at your docs usually won't know about all the intricacies of Next.js interacting with Suspense. Most users will just copy paste. I had several conversations with an LLM about this topic and it always assumes at first that the server wouldn't fetch, wouldn't store, wouldn't suspend, etc. This indicates that general human intuition would likely have the same assumptions.

Suggestions to fix

  • Moving the cache into a useRef + context. That would be re-constructed on every render and therefore fix the security issue.
  • Document the double fetching behaviour clearly. Or find a workaround such as moving the api fetch into a useEffect, which wouldn't run on the server.
  • Potentially negotiate something with the Next.js devs directly to make server rendering handle this differently. For example, I think it would make sense that the server html would include only the loading indicators and not fetch the data. But I believe that might be tricky in terms of implementation. Also because the js engine would run the promise trigger function before touching any React or Next.js functions.

Guia de contribuição

Abrir o guia de contribuição

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Direção de pesquisa

Revise a página de referência de Suspense em https://react.dev/reference/react/Suspense, com foco no exemplo fetchData e em seu cache global. Reproduza o comportamento seguindo as etapas do app-router do Next.js descritas na issue e, em seguida, atualize a documentação para que a duração do cache e o comportamento de fetching no servidor e no cliente sejam precisos, e para que o exemplo não sugira um tratamento inseguro de dados autenticados.

Escrita pelo modelo de indexação a partir do texto da issue.

Avaliação

Stack de tecnologia
javascript, next.js, react
Domínio
documentation, security
Tipo de issue
Documentação
Dificuldade
4/5
Tempo estimado
3-5 dias
Status de atividade
Estagnada
Clareza
Razoavelmente clara
Facilidade para iniciantes
35/100

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.