CVE ID: CVE-2026-21883
Vulnerability: Origin validation bypass in Bokeh server’s WebSocket handling
Severity: High / Critical-class
CVSS Score: ~9.1 (reflects high impact when exploited)
Exploitability: Practical in real-world deployments
Public Exploit / PoC: Not widely circulated, but the bypass logic is simple enough that a proof-of-concept can be constructed.
Mitigation: Patch / Upgrade to fixed Bokeh version (see official patch link at the end).
What is the vulnerability in simple terms?
Bokeh is a Python library commonly used to build interactive visual dashboards. When you run a Bokeh server, it opens a WebSocket endpoint so browsers can connect and update visualizations in real time.
To prevent unauthorized cross-origin access, Bokeh has a feature where it checks the Origin header sent by a browser before allowing a WebSocket connection. This check is meant to ensure only trusted front-ends can connect.
In certain versions of Bokeh before 3.8.2, the Origin check was implemented incorrectly. Rather than requiring the Origin to exactly match what was configured, the comparison logic treated any host that starts with the expected domain as acceptable. For example, if a trusted host was configured as:
dashboard.corp
A malicious host like:
dashboard.corp.attacker.com
would sneak through the check because the flawed logic matches the beginning of the string. This effectively allows a cross-site WebSocket connection to succeed from an untrusted page.
Why does this matter?
WebSockets are long-lived, bidirectional channels. When a browser connects to a WebSocket, it sends session cookies and authentication tokens just like it would for a normal web request. If a malicious page can trick a user’s browser into connecting to a WebSocket that the server accepts, the attacker can piggyback on the user’s authenticated session.
This is called WebSocket Hijacking or Cross-Site WebSocket Hijack (similar in spirit to classic Cross-Site Request Forgery but for WebSockets). The attacker can then:
- Send commands to the server as if they were the victim.
- Receive data meant for the victim.
- Trigger server-side logic tied to that session.
The point is: the attacker does not own the WebSocket connection directly — the victim’s browser does — but the server treats it as legitimate.
How can it be exploited in the real world?
Here’s a realistic exploitation chain:
- A vulnerable Bokeh server is publicly reachable and uses an allowlist of origins (trusted domains).
- An attacker identifies the exact origin pattern used by the server (for example, a subdomain of
corp.com). - The attacker registers a domain or craft a subdomain that prefixes the trusted origin, for example:
corp.com.attacker42.com - The attacker then hosts a malicious page on that domain with JavaScript that opens a WebSocket to the Bokeh server.
- The attacker sends that link to a victim or entices them to click it.
- When the victim’s browser executes the malicious JavaScript, it tries to open a WebSocket to the Bokeh server.
- The Bokeh server accepts the connection because the flawed origin check thinks the origin is allowed.
- From that point, the attacker’s page can interact with the Bokeh WebSocket as if it were the victim.
If the victim has a login session or authentication token, the attacker’s page leverages it to send commands over the hijacked WebSocket.
This is not a remote code execution bug in the server code itself, but it is a bypass of access control that can lead to unauthorized actions with the victim’s privileges.
Exploit / PoC status (educational)
At the time of writing, a widely published exploit script or PoC toolbox does not appear in major vulnerability repositories. That does not mean the flaw is safe — the logic flaw is simple enough that a proof-of-concept can be developed from scratch by anyone familiar with WebSocket client code.
A PoC for this vulnerability would typically consist of:
- A small HTML page that runs JavaScript.
- The JavaScript script opens a WebSocket to a target Bokeh host.
- The script sets the
Originheader automatically when the browser does — browsers setOriginthemselves. - If the bypass works, the attacker’s script can then send and receive messages.
Note: Browsers do not allow direct control of the Origin header in WebSocket handshakes from JavaScript; the bypass relies on how Bokeh interprets the domain string coming from the browser, not on forging headers.
Such a PoC would be used in a controlled lab environment for research and verification, never on live systems without permission.
How could you detect attempts to exploit this?
Detection must focus on behavior that deviates from normal connections. Because WebSockets are stateful and long-lived, you need to inspect the handshake phase and look at headers and hostnames.
Important log sources
To detect exploitation or attempted exploitation, monitor:
- Web server / reverse proxy logs (e.g., nginx, Apache)
- Look for WebSocket handshake requests (
Upgrade: websocket). - Examine the
Originheader. - Record both
HostandOriginvalues.
- Look for WebSocket handshake requests (
- Bokeh application logs
- Enable detailed logging around WebSocket connections.
- Log the raw
Originheader and the resolved allowlist decision.
- Network monitoring (IDS / IPS)
- Tools like Zeek/Bro, Suricata, or Snort can inspect the HTTP handshake part of WebSockets and extract headers like
OriginandHost.
- Tools like Zeek/Bro, Suricata, or Snort can inspect the HTTP handshake part of WebSockets and extract headers like
- Firewall / WAF logs
- If a WAF is upstream, configure it to log suspicious header patterns.
Behavioral indicators to look for
Raise alerts if you see patterns like:
- A WebSocket handshake from an
Originthat is similar to a trusted domain but has extra components — e.g.,dashboard.corp.attacker.comwhen the server’s host isdashboard.corp. - Frequent WebSocket connections from unusual domains or IPs not associated with normal usage.
- Successful WebSocket handshakes where the
Originand server host do not exactly match. - Sudden increases in WebSocket connection attempts after an external referral.
Detection logic examples
Here’s how analysts think about detection:
- Exact match only: If the server is
app.example.com, then only allow connections where theOriginis exactlyhttps://app.example.com. Anything else should be logged and blocked. - Reject prefix tricks: For any
Origin, split the hostname into segments and make sure it does not start with the allowed value unless it’s exactly the same domain. - Whitelist strictness: Avoid loose patterns like
*.example.comunless you understand how the matching logic will work prior to mitigation.
In other words, don’t trust any hostname that merely begins with the trusted text — require exact matches, and alert when something looks like a crafted prefix domain.
Detection signatures ideas
Here are conceptual examples of detection logic:
Web server log search
Look through access logs for WebSocket handshakes where the origin looks unusual:
grep -i "Upgrade: websocket" /var/log/nginx/access.log | grep -i "Origin:"
Then manually examine entries where Origin does not exactly match the expected domain.
Network traffic inspection
Set up rules that extract Origin from WebSocket handshake frames and compare to expected hostnames. Alert when they don’t match exactly.
In Suricata/Snort style rules, you might watch for:
http header containing "Upgrade: websocket"
AND
Origin header that does not match host
App logs
Instrument your Bokeh server to print out:
WebSocket handshake attempt from origin: <value>
Allowed? <yes/no>
Then feed these into your SIEM and create alerts where Allowed? is yes but Origin does not exactly match trusted domains.
What makes a good detection rule?
- The rule should enforce exact match, not prefix match.
- The rule should alert when any of the following occur:
- Hostname in the Origin header does not match the configured trusted host exactly.
- The Origin header contains extra suffixes or subdomain components.
- The number of segments in the hostname is greater than expected.
Example in plain logic:
if WebSocket handshake AND
Origin is present AND
OriginHost != ServerHost then
generate alert
How can this vulnerability be reliably mitigated?
The only reliable long-term fix is to upgrade to a patched version of Bokeh where the origin validation logic has been corrected.
Short-term compensating controls include:
- Place a reverse proxy (nginx/Apache) in front of Bokeh that enforces strict
Origin== expected host before forwarding. - Restrict public access; use VPNs or internal networks when possible.
- Harden allowlists to exact hostnames.
- Monitor and alert on suspicious handshake patterns as described above.
Is there an official patch or upgrade?
Yes. The vulnerability has been fixed in a newer version of Bokeh. You should upgrade your Bokeh installation to the latest patched release.
Official Patch / Upgrade:
https://github.com/bokeh/bokeh/security/advisories/GHSA-793v-589g-574v
Final takeaway
- This flaw lets WebSocket origin checks be bypassed under certain configurations.
- Exploitation is possible when an attacker can lure a logged-in user to a crafted web page.
- The attacker’s page establishes a WebSocket that the server mistakenly accepts.
- Once accepted, messages over that WebSocket run with the victim’s privileges.
- Detection requires careful inspection of handshake headers.
- The fix is to upgrade and tighten how allowed origins are validated.
