Build1 distinct publisher3 min readPublished
The upgrade request is an HTTP GET that carries session cookies cross-origin with no preflight, so CORS never sees it, and the frameworks that could check the Origin header for you mostly hand that job to your route code.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
The Origin header does arrive. RFC 6455 has the browser attach it to the handshake, and no CORS restriction acts on what it says [2]. CORS governs XMLHttpRequest and fetch; the `WebSocket()` constructor sits outside both, with no preflight to fail and no `Access-Control-Allow-Origin` to check [3]. So the browser hands your server the one piece of evidence that would settle the question, and whether anything compares it against an allowlist is a property of your handler, not of the protocol.
Express with the `ws` library exposes a `verifyClient` callback, but nothing requires it, and the default accepts any Origin [6]. The hook exists, and calling it is left to the developer. FastAPI and Starlette have no built-in Origin validation for WebSocket routes at all, and their `CORSMiddleware` applies to HTTP routes only, so the check has to be manual code in the route handler [9].
Socket.io is the one entry with a version boundary. Releases before 2.4.0 accepted all origins by default, tracked as CVE-2020-28481, and 2.4.0 disabled CORS by default, though what that default governs is HTTP responses [10]. The writeup's account breaks off there [10], so it does not establish whether the handshake itself is validated. Counting Socket.io as fixed means reading its release notes closely, since the CVE title covers only part of the picture.
What the gap has actually produced is on record. CVE-2023-0957 hit Gitpod before release-2022.11.2.16, where the JSONRPC API ran over WebSocket with cookie authentication and no Origin check; Snyk's team combined the hijack with a SameSite subdomain bypass to take over a workspace, including code execution [15]. CVE-2024-51775 covered Apache Zeppelin 0.11.1 at CVSS 7.5, where remote network access with no authentication and no user interaction was enough to read paragraph content out of authenticated sessions [16]. The dev.to writeup's diagnosis of the pattern is organisational: cookie authentication works on the HTTP routes and becomes the assumed boundary, then a socket gets added later, often by a different team, without mirroring the CSRF controls already there [17].
For that framework survey to describe your service, two things have to hold. Your handshake has to authenticate from cookies the browser will attach for a page you do not control [2], and your clients have to be browsers, since browser-set Origin is the whole basis of the check [2]. A socket that takes a bearer token in its first application frame fails for reasons unrelated to Origin validation.
Christian Schneider formalised the attack in 2013 under CWE-1385, and James Kettle's PortSwigger material is the canonical public teaching reference [11][12]. The Zeppelin CVE landed eleven years after that [18]. Gitpod shipped its fix within 24 hours of disclosure [15], which is the turnaround typical of an allowlist fix rather than a redesign.
Ranked by verification strength, evidence, and original report placement.
The WebSocket upgrade is an HTTP GET; browsers execute it cross-origin without CORS preflight, carrying full session cookies.
RFC 6455 defines the handshake as an HTTP Upgrade GET; the browser sends the Origin header but no CORS restriction applies.
CORS covers XMLHttpRequest and fetch(); the WebSocket() constructor bypasses both entirely, with no preflight and no Access-Control-Allow-Origin check.
CSRF is blind: the attacker sends a request and cannot read the response. CSWSH establishes a persistent bidirectional channel: the attacker receives all server push messages and can send arbitrary frames as the victim.
Express with the ws library exposes a verifyClient callback, but it is not required; the default behaviour accepts any Origin.
The WebSocket upgrade bypasses the Express middleware stack entirely, requiring explicit validation inside the upgrade handler or verifyClient.
Distinct publishers with included, body-backed reporting in this cluster.
1 article · September 5, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
GitLab bundles a zero-click GraphQL flaw with a CSRF bug, and only one needs a victim1 distinct publisher
build
ShieldBreak: a Defender-to-SYSTEM PoC that your last patch cycle did not stop1 distinct publisher
build
authentik checked the PKCE verifier only when the request bothered to include one1 distinct publisher
build
A query string smuggled into the Host header makes Starlette skip authentication1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Checkable mechanism, single witness
The protocol half of this holds up on its own terms: the handshake behaviour is in RFC 6455, each framework's default is in that project's documentation, and the four CVEs arrive with product names, affected versions and, in Zeppelin's case, a score. That is more specificity than this genre usually offers. It is also all sourced to one developer-platform post, and the tally in its headline cannot be reconciled with the six stacks it then walks through, which is the kind of slip a second pair of eyes catches.
No prevalence measured
LogRhythm, Gitpod, Zeppelin and Socket.io establish that the bug class reaches shipped software over a four-year span, but they say nothing about what share of live WebSocket endpoints authenticate by cookie and skip the Origin check, and no scan, survey or telemetry stands in for that number. This reporting counts nothing that would tell you how common the exposure is.
Headline firmer than the walkthrough
The overstatement is small and specific. A clean four-of-five ratio sits above a survey of six stacks in which Gorilla does compare the Host header and Socket.io's behaviour changed at 2.4.0, so the body is more qualified than the title. The detection section makes the same move, asserting zero false positives and then conceding that curl, Postman and server-to-server clients send no Origin at all. Even the timeline drifts: twelve years in the prose, eleven by the dates given.
Attention, not a sales pitch
Snyk and PortSwigger appear as the researchers who did the work, not as sponsors, and the recommended fix is code the reader writes rather than a tool the reader buys, so no product is being sold here. The pressure on the framing comes from the venue: a countable headline travels further on a developer feed than an accurate summary of defaults that vary by stack, and that is precisely where the piece stretches.
Hold the mechanism, not the tally
We would repeat the handshake and framework-default claims without hedging, because each can be settled in an afternoon against a specification and four sets of project docs. The Gitpod fix window, the Zeppelin score, the LogRhythm privilege context and the four-of-five ratio all depend on a single unreviewed post, and the causal story about a second team adding the endpoint later is asserted rather than shown.