Hacker News new | ask | show | jobs
by btown 2221 days ago
This is why you don't let @wongmjane inject code into websites. Imagine what features she'd learn about with tracebacks from developer machines! /s

In seriousness, this is all because websockets aren't bound by CORS, for good reason. https://blog.securityevaluators.com/websockets-not-bound-by-...

There's a simple fix though - hot reload websocket listeners like Webpack should only consider the connection valid if they first receive a shared secret that's loaded into the initial dev bundle, which itself would never be transmitted over a websocket and could be set via CORS to not be accessible to non-whitelisted origins. It's a dead-simple protocol with no ongoing performance impacts. But understandable it hasn't been implemented yet.

3 comments

> In seriousness, this is all because websockets aren't bound by CORS, for good reason. https://blog.securityevaluators.com/websockets-not-bound-by-...

As far as I can tell, that article only explains that WebSockets aren't bound by CORS. It doesn't provide a reason (good or otherwise) why WebSockets were designed that way. Personally, I consider that feature to be a design flaw. If WebSockets handshakes respected the Same-Origin-Policy and CORS headers the same way every other HTTP request on the web does, none of these vulnerabilities with poorly implemented WebSockets servers would exist today, as they would be secure by default rather than "insecure unless the server properly validates the origin header on every handshake".

Probably too late to do anything about that anymore though. Changing WebSockets to respect the Same Origin Policy now would break a ton of websites.

It's a function of the natural evolution of things.

The same origin policy was originally introduced with AJAX at a time when the vast majority of traffic was to the same origin. It wasn't a common pattern to make complex requests to a different domain (barring GETs and FORM posts which were allowed).

Web development changed and it started to become more popular to make complex cross-domain requests, but the problem is they couldn't just throw out the SOP without introducing a massive security vulnerability to all existing sites. So instead CORS was introduced as an option to relax the SOP.

With websockets being completely different, it was an opportunity to "start over". They opted to embrace cross-domain communication and require developers implement security on top of it.

The `postMessage` API has done the same thing. Any window can `postMessage` to any other window -- it's fully up to the windows to validate the security of messages coming their way.

Some argue that it's a bad idea to make "allow by default" the new paradigm. Personally, it seems pretty clear to me that developers just don't understand CORS at all [0], and letting the developers handle this in their own logic, while being exceptionally clear about this in the documentation, is a far more developer friendly and simple (therefore more secure) approach.

[0]: https://fosterelli.co/developers-dont-understand-cors

Developers not understanding CORS is simply all the more reason why it's good that CORS defaults to secure behavior whenever possible. The harder you make it for ignorant developers to shoot themselves (and their users) in the foot, the better.

postMessage's API design has similar issues to WebSockets. MDN does a pretty good job of explaining the dangers (practically every other paragraph on the page for postMessage contains a warning in bold text about properly validating the origin argument), but I think it would have been far better if the API forced devs to explicitly specify which origins they want to receive events from up front rather than relying on them to check the origin themselves after the message is received. I have little doubt there will be numerous vulnerabilities instigated by the current API design, despite MDN's warnings.

> Developers not understanding CORS is simply all the more reason why it's good that CORS defaults to secure behavior whenever possible. The harder you make it for ignorant developers to shoot themselves (and their users) in the foot, the better.

Right, but not understanding something doesn't mean it is more difficult to shoot yourself in the foot -- in fact it's the opposite. The zoom vulnerability is an example of this, or every developer that just imports `cors()` middleware and runs it because "otherwise it gives some CORS error".

I'd rather an approach that is simple to understand, gives flexibility to the developers, and makes it crystal clear to them what their responsibilities are.

I'm not trying to saying that CORS is harder to shoot yourself in the foot with because it's hard to understand. Rather, it's harder to shoot yourself in the foot with because not understanding it usually just means your site's pages aren't accessible to other origins at all. (That's one reason why so many developers seem to have so much trouble with it; rather than breaking their site's security and remaining completely unaware of it, they instead break their site's functionality instead and need to spend a bunch of "extra" time learning about CORS and the same origin policy before they can get it to work.)

In contrast, not understanding WebSockets usually means your WebSocket endpoints are completely insecure, with no indication that anything is wrong and no incentive to learn more because "it's already working".

Obviously secure-by-default is not completely foolproof. After all, defaults are, by definition, possible to change. But if developers are going to shoot themselves in the foot, I'd much rather them be forced to do so explicitly than have it happen to them implicitly without any action on their part.

I see what you're saying and agree with the fundamentals. I think we disagree on this assumption:

> they instead break their site's functionality and need to spend a bunch of "extra" time learning about CORS and the same origin policy before they can get it to work

In my experience, a very large percentage of developers don't do this. They try that, then find it confusing, and they only estimated two days for this task, which is already late, so they just sort out enough to get it to work. For most cases, this is importing `cors()` and passing it in as middleware. The easiest config... and also the one that makes your site available to all origins.

At the end of the day it will always come down to developer education. Someone will make something easy to use. So we might as well make it really simple to use and understand, so that it's easier to educate the right way to build things.

The last time I was getting annoyed with CORS preventing me from doing something, I wrote a VBA script that opened IE, loaded a random page from the site that was clashing, and injected javascript to do what I wanted.

...and I don't even have admin access to my own computer.

That’s exactly the solution I was thinking of. No end-user visible changes required, just change websocket to require a secret on initial connection. An easy way of doing this might be to use the web socket URL path or a query variable. Note that we’re relying on the websocket library code to do the right thing: https://tools.ietf.org/html/rfc6455#section-10.7

Example, and note: https://news.ycombinator.com/item?id=23261309

Websocket protocol defines Origin header to indicate which website tries to establish connection. Hot reload websocket server must check it and allow localhost connections only (at least by default).
It might not be localhost or a local IP if users use a different hostname, common for some environments, at which point it would have to be configurable. But yes, that could also work, if all browsers send Origin headers as expected.