How this bug class keeps getting shipped
Every few months a new project ships a login path that checks whether a credential is present and calls that authentication. The check runs, the request continues, a session gets created, and the credential is never compared against anything. The bug class is old enough to have its own CWE (CWE-287, Improper Authentication) and its own variants (CWE-288, CWE-305, CWE-798). It keeps landing in shipped code not because engineers misunderstand the definition, but because modern service scaffolds make the failure mode look like a completed feature. This post examines one instance added to CISA's Known Exploited Vulnerabilities catalog on 2026-09-02, then covers the systemic pattern and the detections that catch it.
The lead: CVE-2026-59822 in BerriAI LiteLLM
CISA's KEV entry for CVE-2026-59822 describes an improper authentication vulnerability in the MCP Streamable HTTP endpoint of BerriAI LiteLLM. The advisory states the flaw "could allow an unauthenticated attacker to establish an authenticated MCP session using an arbitrary Bearer token." "Arbitrary" is the operative word: the server accepted a Bearer value with no relation to any secret it should have known and treated the resulting session as authenticated. The issue was not a leaked token, a cross-tenant token, or an expired token.
That distinguishes CVE-2026-59822 from the far more common story of credential reuse. No key rotation would have helped; no vault could have held the secret more tightly. The verification step itself was the defect. With a session established, the MCP endpoint exposes tool invocations to the caller with whatever privileges the endpoint grants. For an AI gateway in front of a language-model backend, that surface is consequential: tool calls, prompt inspection, key material forwarded to upstreams, and any function the gateway executes on the caller's behalf.
What the failure mode looks like in code
The advisory does not publish an exploit, so the following is a generic illustration of the class rather than the specific vulnerable diff. The failing pattern fits on a screen:
async def authenticate(request):
header = request.headers.get("authorization", "")
token = header.removeprefix("Bearer ").strip()
if not token:
raise HTTPException(status_code=401)
# session is created without verifying the token against any secret
return Session(user="mcp-client", scopes={"*"}, authenticated=True)
Every symptom of a working authentication layer is present: an Authorization header parse, a scheme check by prefix, a 401 for the empty case, a returned session with populated fields. The middleware above this function will forward the session down the request pipeline, and downstream handlers will treat session.authenticated as authoritative. What the code does not do is compare the token to anything: no HMAC, no JWK lookup, no database query, no upstream introspection call.
Variants of this shape recur across ecosystems:
- A JWT decode called with
verify=Falsebecause someone was debugging and forgot to flip it back. - A
try/exceptthat catches signature-verification errors and falls through to a "guest" identity that shares an ACL with real users. - A route decorator inherited from a permissive base class and silently missing on the specific handler that ships MCP or admin functionality.
- A reverse proxy that terminates authentication upstream, combined with a service that trusts a downstream header the proxy was supposed to have stripped.
Any one of these compiles, boots, passes smoke tests, and answers the health check with a 200. The missing assertion is invisible in every artifact except a request that tests for it.
Why the class keeps shipping
Two structural forces sustain this class.
First, authentication is usually plumbed by scaffolding: a middleware, a decorator, a framework hook. Once that scaffolding is in place, adding a new endpoint feels like a routing change, not a security change. The MCP Streamable HTTP transport illustrates this: it is often bolted onto an existing service that already has an authentication path for its REST or WebSocket routes. When the middleware chain for the new transport is registered separately, the default becomes "no auth" until someone notices.
Second, auth tests typically cover the boundary case of "no header" and the positive case with a known-good token. Few test suites include the case that catches this bug: a syntactically valid header carrying a value the server has never issued. A test that sends Authorization: Bearer aaaaaaaa and asserts a 401 would have caught CVE-2026-59822 at CI time. Most projects omit that test because "an arbitrary string was accepted" falls outside the mental model of the person writing the fixtures.
The pattern is not confined to AI tooling. The same KEV batch on 2026-09-02 lists CVE-2026-82329 in JFrog Artifactory, described by CISA as an "improper authentication vulnerability that under default configuration can allow an unauthenticated attacker with network access to obtain administrative privileges." Different product, different code, same category: the authentication decision does not gate what it is documented to gate. In one case acceptance is over-broad on the Bearer path; in the other it is over-broad on the default configuration path. Both produce the same result: an unauthenticated caller with authenticated privileges.
How to tell if you are exposed
For CVE-2026-59822 specifically, the vendor advisory is the source of truth on affected versions; upgrade to the patched release. Beyond patching that CVE, treat the class as a recurring risk and add two habits:
- Add a negative-authentication test that submits a well-formed credential the server has never issued. For Bearer schemes, send a random 32-byte hex string and assert a 401. For session cookies, send a signed-looking value with a wrong secret. Run it against every endpoint in the OpenAPI or MCP tool manifest, not just the ones documented on the wiki.
- Instrument authentication decisions, not just authentication outcomes. Log the identifier the token resolved to (key id, issuer, subject) alongside the request. A stream of successful sessions with
sub=Noneorkid=Noneis the fingerprint of a fail-open path and is straightforward to alert on. A query onevent=auth.success AND subject_id IS NULLwill surface most instances of this bug class in a running system, even ones that predate the current advisory.
An external check against an example.com deployment is a one-line probe:
curl -sS -o /dev/null -w '%{http_code}\n' \
-H "Authorization: Bearer $(openssl rand -hex 16)" \
https://mcp.example.com/mcp/stream
Any response other than 401 or 403 on a route that requires authentication warrants investigation. That includes a 200 with an empty body and a 500 that indicates the request made it past the auth layer into a handler.
Closing
CVE-2026-59822 will be patched, the KEV entry will age, and next quarter will bring a new project that ships an endpoint whose auth middleware verifies nothing. The defense is not vigilance about a specific product; it is a small set of tests and log queries that treat "authentication happened" as a claim to verify rather than a state to trust. Improper authentication is a bug class, not an accident, and it responds to being engineered against.
Verifiable security.