Skip to content

web,websocket: Unmerge check_origin methods - #3768

Merged
bdarnell merged 2 commits into
tornadoweb:masterfrom
bdarnell:claude/peaceful-brahmagupta-sbnb6g
Oct 7, 2026
Merged

bdarnell merged 2 commits into
tornadoweb:masterfrom
bdarnell:claude/peaceful-brahmagupta-sbnb6g

Conversation

@bdarnell

@bdarnell bdarnell commented Oct 6, 2026

Copy link
Copy Markdown
Member

In an effort to offer a unified RequestHandler.check_origin for both regular XSRF protection and cross-origin websocket protection, I had changed the semantics of the existing WebSocketHandler.check_origin method. Among other things, it moved from being called after prepare() to before, but this is backwards-incompatible in ways that break jupyter_server (possibly among others)

This PR undoes the merger of the two methods, leaving WebSocketHandler.check_origin to be called after prepare(), and a new pre-prepare hook named RequestHandler.check_trusted_origin to be used for XSRF protection in regular handlers.

The behavior of WebSocketHandler.check_origin has still changed in some ways to be closer to the new check_trusted_origin, where the changes seem unlikely to break existing users. check_origin is no longer called at all if other signals such as the Sec-Fetch-Site header tell us that this is a same-origin request, and its default implementation consults the trusted_origins list that it shares with check_trusted_origin.

claude added 2 commits October 6, 2026 20:38
…eck_origin

Folding WebSocketHandler.check_origin into RequestHandler (to share it
with cross_origin_protection) changed when it is called: it ran in
RequestHandler._execute, before prepare(), instead of in
WebSocketHandler.get(), after prepare(). Applications such as Jupyter
Server override check_origin in ways that depend on prepare() (to
exempt token-authenticated requests), and these overrides failed with
500 errors. Because Jupyter defines check_origin on all of its
handlers, enabling cross_origin_protection would also have called it
before prepare() for every cross-origin POST.

The two hooks have different contracts, so give them different names:

- RequestHandler.check_trusted_origin is the new hook for
  cross_origin_protection. It is called before prepare() and must
  depend only on the request itself. Its default checks the
  trusted_origins setting.
- WebSocketHandler.check_origin is restored and is again called from
  get() after prepare() (and after the Upgrade/Connection header
  checks). It keeps the new 6.6 semantics: it is skipped for requests
  that are same-origin according to Sec-Fetch-Site or the Origin/Host
  comparison, and its default calls check_trusted_origin.

Non-websocket handlers that receive a GET with an Upgrade: websocket
header are no longer subject to the origin check, and rejected
websocket origins are once again logged at debug level with a
"Cross origin websockets not allowed" response body, as in 6.5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P5zJUPT4vE2M6GK7gqHcqo
An override that exempts requests carrying a token must verify the
token, or an attacker can add an invalid one to a cross-site request
and have it authenticated by the victim's cookies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P5zJUPT4vE2M6GK7gqHcqo
@bdarnell

bdarnell commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

@andrii-i (I'm tagging you because you opened #3730)

FYI this is not a victory for the new downstream testbed; I just asked claude to look at the changes between master and v6.5.10 with an eye for things that might be backwards-incompatible and it said that jupyter_server relies on check_origin being called after prepare().

@bdarnell
bdarnell merged commit d283047 into tornadoweb:master Oct 7, 2026
17 checks passed
@bdarnell
bdarnell deleted the claude/peaceful-brahmagupta-sbnb6g branch October 7, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants