CVE-2026-42208: Your AI Proxy's Auth Check Was the Backdoor
LiteLLM's API key verification had a pre-auth SQLi (CVSS 9.3). Six critical vulns in one month. Your AI proxy is now a high-value target.
On this page
You trust your LLM proxy to verify API keys. That’s its job. LiteLLM’s verification code concatenated your Authorization header straight into a SQL query. No parameterized binding. No escaping. Just raw string concatenation in the one function designed to keep attackers out. CVSS 9.3, pre-auth, zero interaction required. The lock on your front door was made of cardboard.
What Happened
CVE-2026-42208 is a pre-authentication SQL injection in LiteLLM’s proxy server. Tencent YunDing Security Lab discovered it, and it’s about as bad as SQLi gets.
Here are the facts:
- CVSS v4 Score: 9.3 (Critical)
- CWE: CWE-89 (SQL Injection)
- Affected versions: v1.81.16 through v1.83.6
- Fixed in: v1.83.7+
- Attack vector: Network, low complexity, no authentication, no user interaction
- GHSA: GHSA-r75f-5x8p-qvmc
An attacker sends a crafted Authorization header to any LLM endpoint — say, POST /chat/completions. The request doesn’t need a valid key. It doesn’t need any key. The proxy’s error-handling path takes the raw header value and drops it into a SQL query like a toddler dropping a fork into a power outlet.
The vulnerable pattern looked something like this:
# The "security" code that was actually a backdoor
# (I wish I were joking)
async def verify_key(api_key: str):
query = f"SELECT * FROM keys WHERE key = '{api_key}'"
result = await db.execute(query)
return result
The fix is what it should have been from day one:
# Parameterized query — the bare minimum for 2026
async def verify_key(api_key: str):
query = "SELECT * FROM keys WHERE key = ?"
result = await db.execute(query, [api_key])
return result
Successful exploitation gives an attacker full read-write access to the proxy’s database. That database stores API keys for every LLM provider you’ve configured — OpenAI, Anthropic, Google, Cohere, all of them. One crafted HTTP header, and someone owns your entire AI budget.
The Error-Handling Blind Spot
Here’s the part that should keep you up at night: this vulnerability didn’t live in the main authentication logic. It lived in the error-handling path.
Think of it like installing a steel vault door on your house, then leaving the alarm system’s control panel accessible through an unlocked window. The happy path — valid key, successful auth — was fine. The sad path — invalid key, log the failure — was where everything fell apart.
Why does this happen? Three reasons:
Developers test success. Your test suite probably has ten cases for “valid key returns 200” and one for “invalid key returns 401.” Nobody writes a test for “invalid key with SQL metacharacters in the error logger.”
Security reviews focus on auth middleware. When a security engineer audits your proxy, they look at the authentication decorator, the JWT validator, the rate limiter. They rarely trace what happens when auth fails and the request falls through to error logging.
Error handlers “just log stuff.” Developers treat error-handling code as low-risk because it doesn’t make decisions. But the moment that “logging” code touches a database, it’s making decisions — it’s just making them without anyone watching.
# Looks harmless. Isn't.
async def log_failed_auth(request):
api_key = request.headers.get("Authorization", "unknown")
# This "harmless" log entry is a SQL injection vector
await db.execute(
f"INSERT INTO error_logs (key, endpoint, ts) "
f"VALUES ('{api_key}', '{request.path}', NOW())"
)
The fix isn’t just parameterizing this one query. It’s auditing every path that user input can reach — especially the ones you think are “just logging.”
Six Vulns, One Month — A Pattern
CVE-2026-42208 didn’t happen in isolation. LiteLLM had six security advisories published in April 2026 alone, plus a supply chain attack. That’s not a bad month — that’s a pattern.
| GHSA ID | Severity | Type |
|---|---|---|
| GHSA-r75f-5x8p-qvmc | Critical | Pre-auth SQL injection (API key verification) |
| GHSA-jjhc-v7c2-5hh6 | Critical | OIDC auth bypass via cache key collision |
| GHSA-v4p8-mg3p-g94g | High | Authenticated command exec via MCP stdio test endpoints |
| GHSA-xqmj-j6mv-4862 | High | Server-Side Template Injection in /prompts/test |
| GHSA-69x8-hrgq-fjj8 | High | Password hash exposure + pass-the-hash auth bypass |
| GHSA-53mr-6c8q-9789 | High | Privilege escalation via unrestricted proxy config endpoint |
On top of that, SentinelOne caught a supply chain attack: someone published trojaned versions v1.82.7 and v1.82.8 to PyPI. A legitimate package, hijacked, weaponized, and pushed to the same registry developers trust for pip install.
The attack surface of a typical LLM proxy is larger than most teams realize:
LLM Proxy Attack Surface
View diagram source
flowchart TD
A[Client Request] -->|Authorization header| B[API Key Verification]
A -->|OIDC token| C[Identity Provider]
B -->|valid| D[LLM Router]
B -->|invalid| E[Error Logger]
D --> F[Provider APIs]
D --> G[Request Cache]
H[Admin API] -->|config changes| D
H -->|user management| B
I[MCP Endpoints] -->|tool calls| D
J[Prompt Templates] -->|render| D
E -->|writes| K[Database]
B -->|reads| K
H -->|reads/writes| K
AI infrastructure projects move fast. LiteLLM adds support for new providers, new auth methods, and new admin features at startup speed. Security review can’t keep pace when the attack surface grows weekly. This isn’t unique to LiteLLM — it’s the story of every AI proxy, gateway, and orchestration layer shipping features faster than they ship audits.
Hardening Your LLM Proxy
If you’re running LiteLLM, the immediate fix is straightforward: upgrade to v1.83.7 or later. The vulnerable query now uses parameterized bindings.
Can’t upgrade right now? Set disable_error_logs: true under general_settings in your LiteLLM config. This removes the exploitable code path entirely. You lose error logging, but you keep your API keys.
Beyond this specific CVE, here’s a hardening checklist for any LLM proxy deployment:
1. Parameterized queries everywhere — including error paths. Audit every db.execute() call, not just the ones in your auth middleware. The ones in your error handlers, analytics, and admin logging are the ones attackers will find first.
2. Audit error handlers with the same rigor as auth code. If your security review skips try/except blocks and error loggers, you’re leaving the back door unlocked. Every code path that touches user input is a security boundary.
3. Pin proxy versions. Don’t auto-update AI infrastructure. Running litellm==latest in production is a supply chain risk. Pin to a specific version, review changelogs before upgrading, and verify checksums.
4. Network-segment your proxy. Your LLM proxy holds API keys for every provider you use. It’s a secrets manager whether you intended it to be or not. Put it behind a private network, restrict inbound access, and monitor outbound connections.
5. Rotate all LLM API keys if you ran affected versions.
If you ran LiteLLM v1.81.16 through v1.83.6 with a database backend, assume your stored API keys are compromised. Rotate every key stored in the proxy’s database — OpenAI, Anthropic, Google, all of them. Check your provider dashboards for anomalous usage.
Here’s what safe error logging looks like:
# Safe: parameterized insert, truncated input, no raw user data in SQL
async def log_failed_auth(request):
api_key = request.headers.get("Authorization", "unknown")
# Truncate to prevent storage abuse, parameterize to prevent SQLi
safe_prefix = api_key[:8] + "..." if len(api_key) > 8 else api_key
await db.execute(
"INSERT INTO error_logs (key_prefix, endpoint, ts) VALUES (?, ?, ?)",
[safe_prefix, request.path, datetime.utcnow()]
)
Your LLM proxy stores credentials for every AI provider you use. It routes every prompt your organization sends. It’s not a convenience wrapper — it’s critical infrastructure. Treat it like a secrets manager. Lock down the network. Audit the error paths.
The bugs that matter aren’t in the code you review carefully. They’re in the code you assume is boring.