Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DoS vulnerability: adapter-node crashes with malformed URI #5090

Closed
kenkunz opened this issue May 26, 2022 · 10 comments
Closed

DoS vulnerability: adapter-node crashes with malformed URI #5090

kenkunz opened this issue May 26, 2022 · 10 comments
Labels
bug Something isn't working p0-urgent SvelteKit is broken or vulnerable for most users

Comments

@kenkunz
Copy link

kenkunz commented May 26, 2022

Describe the bug

Using the latest version of SvelteKit and adatper-node, a malformed URI causes the node process to crash.

We recently encountered this in production on the tradingstrategy.ai.

We implemented a work-around by creating a server.js wrapper and calling the handler function exported by adapter-node. We first tried wrapping handler in a try/catch … this didn't work / the server still crashed. We ended up manually calling decodeURI() in a try/catch to detect the error before calling handler.

I am open to working on a PR for this. There appears to be a test that would catch this issue, but the test setup appears to be broken. If you can advise me on how to get tests working, I can confirm that this test fails and work on a solution.

Reproduction

This is trivial to reproduce with a vanilla SvelteKit app using adapter-node, so in lieu of a repro repo, here are simple steps to recreate…

1. Create vanilla SvelteKit app with adapter-node

Select the most vanilla options: Skeleton, None, No, No, No

npm init svelte adapter-node-crash
cd adapter-node-crash
perl -pi -e 's/adapter-auto/adapter-node/' package.json svelte.config.js
npm install

2. Build and preview

npm run build
npm run preview

3. Hit it with some malformed URIs

3.a. Simple invalid URI

curl http://localhost:3000//

See server logs below.

3.b. Invalid encoded value

curl http://localhost:3000/%CD

See server logs below.

Logs

# 3.a. Simple invalid URI

node:internal/url:553
  throw new ERR_INVALID_URL(input);
  ^

TypeError [ERR_INVALID_URL]: Invalid URL
    at new NodeError (node:internal/errors:372:5)
    at URL.onParseError (node:internal/url:553:9)
    at new URL (node:internal/url:629:5)
    at file:///Users/ken/Code/tradingstrategy/adapter-node-repro/node_modules/@sveltejs/kit/dist/chunks/index5.js:94:25
    at next (file:///Users/ken/Code/tradingstrategy/adapter-node-repro/node_modules/@sveltejs/kit/dist/chunks/index5.js:229:5)
    at file:///Users/ken/Code/tradingstrategy/adapter-node-repro/node_modules/@sveltejs/kit/dist/chunks/index5.js:229:29
    at file:///Users/ken/Code/tradingstrategy/adapter-node-repro/node_modules/@sveltejs/kit/dist/chunks/constants.js:642:28
    at next (file:///Users/ken/Code/tradingstrategy/adapter-node-repro/node_modules/@sveltejs/kit/dist/chunks/index5.js:229:5)
    at file:///Users/ken/Code/tradingstrategy/adapter-node-repro/node_modules/@sveltejs/kit/dist/chunks/index5.js:229:29
    at file:///Users/ken/Code/tradingstrategy/adapter-node-repro/node_modules/@sveltejs/kit/dist/chunks/constants.js:642:28 {
  input: '//',
  code: 'ERR_INVALID_URL'
}



# 3.b. Invalid encoded value

file:///Users/ken/Code/tradingstrategy/adapter-node-repro/.svelte-kit/output/server/index.js:2022
  let decoded = decodeURI(url.pathname);
                ^

URIError: URI malformed
    at decodeURI (<anonymous>)
    at respond (file:///Users/ken/Code/tradingstrategy/adapter-node-repro/.svelte-kit/output/server/index.js:2022:17)
    at Server.respond (file:///Users/ken/Code/tradingstrategy/adapter-node-repro/.svelte-kit/output/server/index.js:2293:12)
    at async file:///Users/ken/Code/tradingstrategy/adapter-node-repro/node_modules/@sveltejs/kit/dist/chunks/index5.js:161:5

System Info

System:
    OS: macOS 12.3.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 79.53 MB / 16.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 16.15.0 - ~/.nvm/versions/node/v16.15.0/bin/node
    Yarn: 1.22.15 - ~/.nvm/versions/node/v16.15.0/bin/yarn
    npm: 8.5.5 - ~/.nvm/versions/node/v16.15.0/bin/npm
  Browsers:
    Brave Browser: 101.1.38.115
    Chrome: 102.0.5005.61
    Firefox: 100.0.2
    Safari: 15.4
  npmPackages:
    @sveltejs/adapter-node: next => 1.0.0-next.76
    @sveltejs/kit: next => 1.0.0-next.345
    svelte: ^3.44.0 => 3.48.0

Severity

serious, but I can work around it

Additional Information

This seems to be a regression of something that worked in the past. See:

@kenkunz
Copy link
Author

kenkunz commented Jun 6, 2022

Any feedback on this? This defect exposes a trivial DoS vulnerability, so I think it warrants some attention. As I mentioned above, I'd be happy to work on a PR to address this if someone can advise me on how to get tests working.

For others experiencing this issue in production, I wanted to share how we are mitigating this until it is fixed in the adapter:
https://github.com/tradingstrategy-ai/frontend/pull/107/files

@kenkunz kenkunz changed the title adapter-node crashes with malformed URI DoS vulnerability: adapter-node crashes with malformed URI Jun 6, 2022
@Conduitry
Copy link
Member

I can't reproduce this with the actual production output with the Node adapter (what you would get when running node build/index.js after building). I do see it happening with preview, as noted in the original issue description.

If this is only affecting preview, then it's still something we should get fixed, but it doesn't seem critical. preview doesn't actually run an adapter version of your app (what's run is the same regardless of which adapter you're using), and it's really not what you should be running in production.

@Conduitry Conduitry added bug Something isn't working p0-urgent SvelteKit is broken or vulnerable for most users labels Jun 6, 2022
@Conduitry
Copy link
Member

Ah, I see there were two different requests mentioned in the OP. Yes, curl http://localhost:3000/%CD does crash the real production Node-adapted output app for me. (curl http://localhost:3000// only crashed the preview app.)

@kenkunz
Copy link
Author

kenkunz commented Jun 6, 2022

@Conduitry good point – I had not noticed the difference between preview and node build/index.js in my own testing. We knew this was breaking for us in prod with the second/malformed URI (where we were correctly starting the service with node build/index.js), so I guess when I saw it break with preview I assumed the behavior was the same.

@Conduitry
Copy link
Member

The preview issue is another instance of the paths beginning with // being treated as a protocol-relative URL, and then new URL fails when there's no protocol. It looks like that part can be handled with

diff --git a/packages/kit/src/core/preview/index.js b/packages/kit/src/core/preview/index.js
index 90adb401..2ff58e4d 100644
--- a/packages/kit/src/core/preview/index.js
+++ b/packages/kit/src/core/preview/index.js
@@ -78,7 +78,7 @@ export async function preview({ port, host, https: use_https = false }) {
 
 		(req, res, next) => {
 			const original_url = /** @type {string} */ (req.url);
-			const { pathname } = new URL(original_url, 'http://dummy');
+			const { pathname } = new URL('http://dummy' + original_url);
 
 			if (pathname.startsWith(base)) {
 				next();
@@ -106,7 +106,8 @@ export async function preview({ port, host, https: use_https = false }) {
 				return;
 			}
 
-			const { pathname } = new URL(/** @type {string} */ (req.url), 'http://dummy');
+			const original_url = /** @type {string} */ (req.url);
+			const { pathname } = new URL('http://dummy' + original_url);
 
 			// only treat this as a page if it doesn't include an extension
 			if (pathname === '/' || /\/[^./]+\/?$/.test(pathname)) {

The more serious issue - the one with the built production app - is coming from an exception thrown at

let decoded = decodeURI(url.pathname);

Presumably we should be trying to catch an error here and just returning a 400 instead.

@kenkunz
Copy link
Author

kenkunz commented Jun 6, 2022

Makes sense! I'd be happy to submit a PR for this, but I'd prefer to first see a failing test first & then make it pass. Any insight on how to get tests working for adapter-node?

@benmccann
Copy link
Member

preview was recently rewritten and doesn't seem to be crashing with the latest when I do curl http://localhost:3000//. You may want to test to verify that I checked correctly

@kenkunz
Copy link
Author

kenkunz commented Jun 24, 2022

Hi @benmccann – just tested preview and confirmed it no longer crashes with curl http://localhost:3000//. It does still crash with malformed encoded URI. Not sure if this is a concern for preview as no one should be running this in prod.

No change when running with node build/index.js – still crashing with malformed encoded URI (but not with //).

@Rich-Harris
Copy link
Member

Nothing is crashing for me with the latest versions of everything, either in preview or production, for // and /%CD — I think it's now safe to close this issue

@kenkunz
Copy link
Author

kenkunz commented Jul 17, 2022

Verified based on latest versions – LGTM 👍.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working p0-urgent SvelteKit is broken or vulnerable for most users
Projects
None yet
Development

No branches or pull requests

4 participants