sveltejs / sveltejs/kit

Attack vector arising from naive developer use of the `+layout.server.js` tree

Open
#6,315 100 comments 76 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
20.8k
Forks
2.3k
Avg merge
1d 16h
Merged PRs (30d)
156

Description

Describe the bug

tldr;

  • The existence of the +layout.server.js tree will lead folks to put logic there rather than in +page.server.js.
  • Because SvelteKit decides on the client what to fetch from the tree, there are edge cases where important (e.g., authorization) logic in +layout.server.js is skipped.
  • This may seem a contrived edge case, but it is an attack vector that needs to be addressed through documentation.

Consider the following case. (Repo link below)

src
└── routes
    └── launch-codes
        ├── +layout.server.js
        ├── +page.server.js
        └── +page.svelte

Let's say I naively (but I think quite naturally) do the following things:

  • I place authentication/authorization logic in the +layout.server.js file. Perhaps I plan to elaborate this section of the site with launch-codes/[id].
  • Meanwhile, I use +page.server.js to fetch a paged list of codes from the database.

Essentially I have separated the concerns of authorization and data collection.

Now this is totally secure as long as my user remains signed in. But let's say the following happens:

  • The user goes to /launch-codes. This first page view runs the authorization logic in +layout.server.ts.
  • They are signed out, say on another tab, or because the cookie expires, or just by deleting the cookie in the console.
  • In the original tab, now signed out, they go to /launch-codes?page=2. The authorization logic in +layout.server.ts is skipped, and the signed out user is shown the launch codes.

I don't think there's a way to reliably fix this in the framework. The client side router decides what to fetch, and that AFAICT will always be manipulable. The only way to "fix" it is by documentation.

(Or we could get rid of the layout data tree notion entirely. Not a bad option, IMO, but a separate losing argument.)

I know all this may seem contrived, but:

  • It's not that far-fetched for a developer to assume that separating authorization concerns is partly what +layout.server.js is for.
  • It's not that far-fetched to assume that some apps may require quick cookie expiration. Think your bank.
  • It exists. It's an attack vector.
Reproduction

Reproduction here, as minimal as I could make it: https://github.com/cdcarson/yellow-submarine.git

git clone https://github.com/cdcarson/yellow-submarine.git
cd yellow-submarine
npm i
npm run dev
  • Go to http://127.0.0.1:5173/launch-codes
  • Assuming you are not signed in, you'll be redirected to a sign in page, and redirected back after clicking the button
  • Navigate through the paginated list
  • In the tab's console, delete the signedIn cookie. (I.e. the cookie has expired or you've been signed out on another tab.)
  • Without refreshing the page, click to another page in the list. You will be shown that page, rather than being redirected to /sign-in

I made a separate route with a "non-naive" implementation, getting rid of layout.server.js and doing the authorization in +page.server.ts.

  • Go to http://127.0.0.1:5173/launch-codes-fixed
  • Replicate the steps above. This time, after deleting the cookie, subsequent navigations through the list at /launch-codes-fixed will be redirected to /sign-in
Logs

No response

System Info
System:
    OS: macOS 12.4
    CPU: (8) arm64 Apple M1
    Memory: 70.06 MB / 8.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.13.2 - ~/.nvm/versions/node/v16.13.2/bin/node
    npm: 8.1.2 - ~/.nvm/versions/node/v16.13.2/bin/npm
  Browsers:
    Chrome: 104.0.5112.101
    Safari: 15.5
  npmPackages:
    @sveltejs/adapter-auto: next => 1.0.0-next.66 
    @sveltejs/kit: next => 1.0.0-next.442 
    svelte: ^3.44.0 => 3.49.0 
    vite: ^3.0.4 => 3.0.9
Severity

serious, but I can work around it

Additional Information

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Clone the linked yellow-submarine reproduction and follow the launch-codes steps, comparing +layout.server.js with the non-naive launch-codes-fixed route and its +page.server.ts authorization. Review the SvelteKit documentation covering server layouts, page loads, and navigation, then document the safe authorization guidance and verify it against the reproduction steps.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
authentication, documentation, security
Issue type
Documentation
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.