VYPR
Moderate severityNVD Advisory· Published Aug 28, 2023· Updated Feb 13, 2025

Open Redirect Vulnerability in jupyter-server

CVE-2023-39968

Description

jupyter-server is the backend for Jupyter web applications. Open Redirect Vulnerability. Maliciously crafted login links to known Jupyter Servers can cause successful login or an already logged-in session to be redirected to arbitrary sites, which should be restricted to Jupyter Server-served URLs. This issue has been addressed in commit 29036259 which is included in release 2.7.2. Users are advised to upgrade. There are no known workarounds for this vulnerability.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Jupyter Server open redirect allows attackers to redirect users to arbitrary sites via crafted login links, potentially leading to credential theft.

Vulnerability

Overview

CVE-2023-39968 is an open redirect vulnerability in the Jupyter Server backend, which powers Jupyter web applications like JupyterLab and Notebook. The root cause is insufficient validation of redirect URLs during the login process, allowing maliciously crafted login links to redirect users to arbitrary external sites after successful authentication or if already logged in [1][2].

Exploitation

An attacker can exploit this by crafting a login link that points to a known Jupyter Server instance but includes a redirect parameter to an attacker-controlled site. When a user clicks the link and logs in (or if they are already authenticated), the server redirects the user's browser to the attacker's site. No special network position is required; the attacker only needs to lure a user to click the link [2]. The blog post by xss.am demonstrates chaining this open redirect with a client-side path traversal and a Chromium bug to leak JupyterLab authentication and CSRF tokens [1].

Impact

Successful exploitation enables phishing attacks, credential theft, or session hijacking. By redirecting users to a malicious site that mimics the Jupyter interface, attackers can capture login credentials or authentication tokens. The chained attack in [1] shows that token leakage is a practical consequence, amplifying the severity of the open redirect.

Mitigation

The vulnerability is fixed in Jupyter Server version 2.7.2, which includes commit 29036259 [2][4]. There are no known workarounds; users are strongly advised to upgrade to the patched version immediately. The PyPA advisory database also lists this vulnerability [4].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
jupyter-serverPyPI
< 2.7.22.7.2

Affected products

4

Patches

1
290362593b2f

Merge pull request from GHSA-r726-vmfq-j9j3

2 files changed · +16 2
  • jupyter_server/auth/login.py+15 2 modified
    @@ -41,12 +41,25 @@ def _redirect_safe(self, url, default=None):
             # \ is not valid in urls, but some browsers treat it as /
             # instead of %5C, causing `\\` to behave as `//`
             url = url.replace("\\", "%5C")
    +        # urllib and browsers interpret extra '/' in the scheme separator (`scheme:///host/path`)
    +        # differently.
    +        # urllib gives scheme=scheme, netloc='', path='/host/path', while
    +        # browsers get scheme=scheme, netloc='host', path='/path'
    +        # so make sure ':///*' collapses to '://' by splitting and stripping any additional leading slash
    +        # don't allow any kind of `:/` shenanigans by splitting on ':' only
    +        # and replacing `:/*` with exactly `://`
    +        if ":" in url:
    +            scheme, _, rest = url.partition(":")
    +            url = f"{scheme}://{rest.lstrip('/')}"
             parsed = urlparse(url)
    -        if parsed.netloc or not (parsed.path + "/").startswith(self.base_url):
    +        # full url may be `//host/path` (empty scheme == same scheme as request)
    +        # or `https://host/path`
    +        # or even `https:///host/path` (invalid, but accepted and ambiguously interpreted)
    +        if (parsed.scheme or parsed.netloc) or not (parsed.path + "/").startswith(self.base_url):
                 # require that next_url be absolute path within our path
                 allow = False
                 # OR pass our cross-origin check
    -            if parsed.netloc:
    +            if parsed.scheme or parsed.netloc:
                     # if full URL, run our cross-origin check:
                     origin = f"{parsed.scheme}://{parsed.netloc}"
                     origin = origin.lower()
    
  • tests/auth/test_login.py+1 0 modified
    @@ -92,6 +92,7 @@ def login(jp_serverapp, http_server_client, jp_base_url, login_headers):
             "//host{base_url}tree",
             "https://google.com",
             "/absolute/not/base_url",
    +        "https:///a%40b/extra/slash",
         ),
     )
     async def test_next_bad(login, jp_base_url, bad_next):
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

1