-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Protect against cross origin websockets. #980
Conversation
Since |
We should definitely make it easy for applications to check the origin header, and it should probably be turned on by default (I'm not 100% convinced here, but I was surprised when I learned that cross-origin websockets were allowed by default so it's probably better to be conservative). A request with no origin header should probably be allowed rather than rejected - browsers always send the origin, so a request without one is coming from a source where the origin header would provide no meaningful authentication anyway. Because of proxies like nginx and haproxy, a tornado server doesn't necessarily know its correct origin (it may be listening on another port than the one the client connects to). We need some way to tell Tornado of its true origin. This could either be another option at the WebSocketHandler level, or perhaps we could put it on HTTPServer to make it a part of the incoming request (by analogy with the existing You added the When comparing origins the default ports |
@bdarnell - Thank you so much for the excellent feedback! I'm never sure how PRs out of nowhere will be received on a project.
It is a protection for endusers on a browser, so that's completely sane. I'll change to match that.
The
Just the feedback I needed. Wasn't quite sure how to structure this. That's a really good idea. Disable by default but let users override how the checking is done, to include checking for particular origins?
In the server I'm using, which is a client side application, the ports are actually high ports and do appear both on the origin and the header. I can provide samples of this directly to you if necessary. I submitted this on the road but I'll be back to it soon, including getting tests to pass (and adding some). |
While adding tests, I noticed how terrible of an interface I made by putting this in _execute. If they override it, they'd have to provide a default argument for it to do anything different.
That's so ugly. This is my first time hacking on Tornado, so I'm a bit lost on how to get to certain data and responses within the tests. In particular, I would want to verify the status code that was returned to the WebSocketClientConnection. Can you point me in the right direction so I can make better, more robust tests? On a side note, I noticed that some of the tests aren't enabled (by way of the |
Good catch on the test_websocket_callbacks bug - that wasn't intentional. I'll fix that and I think I've got a trick to detect and prevent these in the future. For testing the error codes, you should be able to catch the HTTPError raised by websocket_connect - see test_websocket_http_fail. It's not always possible for an application to pass in all its allowed origins (unless perhaps we introduce some sort of wildcard matching, but that just invites complexity), so I'd turn the interface around a bit: |
Cool. That helped. Also made me realize I had the origin check in the wrong spot. Yay for tests.
We won't actually know the port since origin is in the context of the browser. I can try to reach a websocket hosted on localhost using my javascript console while on This is in a happy state now I think. Ready for comments or merging! |
@@ -296,7 +296,7 @@ directory as your Python file, you could render this template with: | |||
self.render("template.html", title="My title", items=items) | |||
|
|||
Tornado templates support *control statements* and *expressions*. | |||
Control statements are surronded by ``{%`` and ``%}``, e.g., | |||
Control statements are surrounded by ``{%`` and ``%}``, e.g., |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops, this typo fix was meant to go in a separate PR. I can move this if necessary.
@bdarnell, just realized this PR was still out and about. Would you still want to bring this in if I address your last few items? |
Yes, I think if you address my last few comments it'll be ready to merge. |
I've addressed the last few comments and performed a rebase to make this easier to merge. |
|
||
# If there was an origin header, check to make sure it matches | ||
# according to check_origin. When the origin is None, we assume it | ||
# came from a browser and that it can be passed on. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean "we assume it did not come from a browser and therefore we do not need to enforce the same-origin policy", right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, "did not come from a browser".
Merged with the handling of non-absolute origins removed. |
Awesome, thanks for working through it with me. Checking out 6403cb9 now. |
custom logging. Fixes tornadoweb#980.
Since CORS headers don't exist in WebSockets land, we need to check origin and host. This adds origin and host checking within the WebSocketHandler.
The opinionated piece of this PR is that cross origin is disabled by default.
If a user wants different behavior, they'll need to override
check_origin
.