Skip to content

CORS is not checked when browsing files. check origin now https://github.com/jupyter-server/jupyter_server/issues/1459 #1465

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

Merged
merged 2 commits into from
Jul 25, 2025

Conversation

gogasca
Copy link
Contributor

@gogasca gogasca commented Oct 25, 2024

Add check_origin check during check_xsrf_cookie for files.
Details: #1459

Summary

This PR addresses a CORS bypass vulnerability in the /files endpoint. The issue allows attackers to bypass
configured CORS restrictions (allow_origin_pat) by exploiting the XSRF token validation flow.

The Vulnerability

The current check_xsrf_cookie() method in base/handlers.py:537 only calls super().check_xsrf_cookie() (Tornado's
XSRF validation) without first checking if the request origin is allowed. This allows attackers to:

  1. Set an XSRF cookie in a shared domain
  2. Make cross-origin requests with matching XSRF tokens
  3. Access /files endpoints despite CORS restrictions

Risk Assessment:

  • Impact: Unauthorized access to server files
  • Exploitability: Requires shared domain configuration but relatively straightforward to exploit
  • Scope: Affects all file access via /files endpoint
  • Mitigation: Simple fix with clear security benefit

@vidartf
Copy link
Member

vidartf commented Nov 7, 2024

@gogasca Thanks for the issue and the PR. I've been reading it, and agree that the allow_origin_pat should be checked in this case as well. That said, in the exception handler there is already some code to call check_referer for GET and HEAD, and the docstring of that method already specifically mentions the /files endpoint, so I think it should probably be the method used here (at least for GET/HEAD). Also, I want to be careful to see if there are any configuration scenarios where the extra check would fail some existing behavior that we still want to allow, so I've been discussing this in the dev call (cc @Zsailer ), so the review might take a bit longer.

Happy for any input you have on the above, and thanks again!

@gogasca
Copy link
Contributor Author

gogasca commented Nov 21, 2024

Thanks @vidartf I will test using check_referer method and get back to you. I have been unable to join the call in the past few weeks due to some personal commitments, but will report back with more details. Thanks

Copy link
Contributor Author

@gogasca gogasca left a comment

Choose a reason for hiding this comment

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

Change self.check_origin() to check_referer()

@gogasca
Copy link
Contributor Author

gogasca commented Jan 11, 2025

After changing: Change self.check_origin() to check_referer() many tests cases are broken, reverting back to self.check_origin()

@gogasca
Copy link
Contributor Author

gogasca commented Jan 15, 2025

@vidartf can you PTAL again?

@Zsailer Zsailer self-requested a review January 23, 2025 16:21
@gogasca
Copy link
Contributor Author

gogasca commented Feb 7, 2025

@vidartf @Zsailer when you have some time, could you PTAL at this PR. Thanks!

1 similar comment
@gogasca
Copy link
Contributor Author

gogasca commented Jun 18, 2025

@vidartf @Zsailer when you have some time, could you PTAL at this PR. Thanks!

@Zsailer
Copy link
Member

Zsailer commented Jul 22, 2025

@gogasca apologies that this has taken so long to review. I updated the top level comment to give more information here.

@vidartf You raise a valid concern about consistency with existing behavior. Here's my analysis:

Key Differences Between Methods

check_origin():

  • Allows requests with missing Origin header (common for same-origin requests, scripts)
  • General CSRF protection for API requests and WebSockets
  • More permissive

check_referer():

  • Blocks requests with missing Referer header
  • Specifically designed for /files/ endpoint (per docstring: "Used on GET for api endpoints and /files/ to block
    cross-site inclusion (XSSI)")
  • More restrictive, better XSSI protection

The Trade-off

Your suggestion to use check_referer() for GET/HEAD requests is technically correct for consistency, but
introduces compatibility risks:

  • ✅ Better security: Purpose-built for /files/ endpoint XSSI protection
  • ✅ Consistency: Matches existing fallback behavior
  • ⚠️ Compatibility risk: May break direct file access, bookmarked files, or scenarios where browsers strip
    Referer headers

My Recommendation

  • Option 1: Hybrid Approach (if we want architectural consistency)
 try:
      if self.request.method in {"GET", "HEAD"}:
          if not self.check_referer():
              raise web.HTTPError(404)
      else:
          if not self.check_origin():
              raise web.HTTPError(404)
      return super().check_xsrf_cookie()
  except web.HTTPError as e:
      # Existing fallback logic remains unchanged
  • Option 2: The current approach is a safe, minimal fix that:
    • ✅ Fixes the immediate CORS bypass vulnerability
    • ✅ Preserves existing fallback behavior
    • ✅ Lower risk of breaking legitimate workflows

Conclusion

I think the current PR is acceptable as-is for the security fix. The architectural improvement (using
check_referer for GET/HEAD) could be addressed in a follow-up PR with more extensive testing to ensure no
regressions.

@Zsailer Zsailer added the bug label Jul 25, 2025
@Zsailer Zsailer merged commit 31a8555 into jupyter-server:main Jul 25, 2025
71 of 74 checks passed
@Zsailer
Copy link
Member

Zsailer commented Jul 25, 2025

Thank you @gogasca for your patience here!

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

Successfully merging this pull request may close these issues.

3 participants