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

Security fix 1794. WS server missing Origin validation. #2494

Closed

Conversation

carlosgeos
Copy link

@carlosgeos carlosgeos commented Jan 2, 2019

↪️ Pull Request

I believe this PR solves #1783 and amends #1794. I think the merged fix was doing nothing basically (this one --> a1a5422)

It was giving Websocket.Server an origin option which is not even accepted by the constructor of Websocket.Server: https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback

This would mean that this (apparently high) security vulnerability is still present up to the latest version as of now (1.11.0)

I used the proper verifyClient hook to check the origin.
I also included a default hmrHostname on cli.js since there was no hmrHostname by default !

💻 Examples

  • Navigating to localhost:1234 works the same as before. Everything works.
  • Some site running malicious JS can't connect to the websocket server to sniff the code changes, since the origin will be example.com and not localhost.

🚨 Test instructions

I adapted about half the HMR tests and they pass.

The other half use the Node vm library and I couldn't find a way for this isolated V8 environment to send an origin header with the WebSocket connection. Therefore they fail. I need some help here 😛

I used the proof of concept found in this blog to test the fix (before and after): https://blog.cal1.cn/post/Sniffing%20Codes%20in%20Hot%20Module%20Reloading%20Messages. I basically set up a remote server, serving some JS that fetches http://localhost:1234 and gets the random port number of the websocket server, then connects to it. All the details are in the blog post.

Of course the issue with the static assets (where the random port is found, actually) and CORS is still there, and I will look into it next.

✔️ PR Todo

  • Added/updated unit tests for this change (PARTIALLY)
  • Filled out test instructions (In case there aren't any unit tests)
  • Included links to related issues/PRs

Carlos Requena López added 2 commits January 1, 2019 20:52
tests use the ws library to establish a websocket connection, and they
have an undefined origin by default. This is changed

tests have no defined hmrHostname so it was set too.
@carlosgeos carlosgeos changed the title Security fix 1794 Security fix 1794. Missing Origin validation. Jan 2, 2019
@carlosgeos carlosgeos changed the title Security fix 1794. Missing Origin validation. Security fix 1794. WS sever missing Origin validation. Jan 2, 2019
@carlosgeos carlosgeos changed the title Security fix 1794. WS sever missing Origin validation. Security fix 1794. WS server missing Origin validation. Jan 2, 2019
@@ -214,6 +214,7 @@ async function bundle(main, command) {

command.throwErrors = false;
command.scopeHoist = command.experimentalScopeHoisting || false;
command.hmrHostname = command.hmrHostname || 'localhost';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change, so not sure if this should have a default

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, it could go on Bundler.js' normalizeOptions like this:

hmrHostname:
        options.hmrHostname ||
        options.host ||
        'localhost',

Instead of:

hmrHostname:
        options.hmrHostname ||
        options.host ||
        (options.target === 'electron' ? 'localhost' : ''),

It simplifies the tests as well I think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would still be a breaking change, but yes. If you really wanna do it than that would probably be a cleaner solution

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is:

.option(
'--hmr-hostname <hostname>',
'set the hostname of HMR websockets, defaults to location.hostname of current window'
)

It says it defaults to window.location.hostname but this is not true. It is only true, for some var hostname later in the code, that is used in the client side code for connecting to the websocket server:

var hostname = process.env.HMR_HOSTNAME || location.hostname;

This default does not reach HMRServer.js (obviously). When the time to check the origin comes, hmrHostname is empty, and the check never takes place. And the check, even if it happened, does nothing, as explained before.

hmrHostname needs to have a default somewhere and normalizeOptions is the best place. I don't see why it's a breaking change: people using a specified hmrHostname (because they are using some complex setup) will still keep that.

Another option: deprecate hmrHostname like you mentioned in #1794 (comment) and #1482 (comment). The flags host or hostname can be used to make the origin check, and as fallback to window.location.hostname in the client.

What do you think ?

Copy link
Member

@DeMoorJasper DeMoorJasper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved this too early, tests are still failing. Please fix them

@DeMoorJasper
Copy link
Member

Closing this as it hasn't been updated in a while and the fix is included in Parcel 2's HMR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants