vLLM vulnerable to timing attack at bearer auth
Description
vLLM is an inference and serving engine for large language models (LLMs). Before version 0.11.0rc2, the API key support in vLLM performs validation using a method that was vulnerable to a timing attack. API key validation uses a string comparison that takes longer the more characters the provided API key gets correct. Data analysis across many attempts could allow an attacker to determine when it finds the next correct character in the key sequence. Deployments relying on vLLM's built-in API key validation are vulnerable to authentication bypass using this technique. Version 0.11.0rc2 fixes the issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
vllmPyPI | < 0.11.0 | 0.11.0 |
Affected products
1Patches
1ee10d7e6ff58Validate API tokens in constant time (#25781)
1 file changed · +24 −4
vllm/entrypoints/openai/api_server.py+24 −4 modified@@ -3,12 +3,14 @@ import asyncio import gc +import hashlib import importlib import inspect import json import multiprocessing import multiprocessing.forkserver as forkserver import os +import secrets import signal import socket import tempfile @@ -1252,7 +1254,7 @@ def load_log_config(log_config_file: Optional[str]) -> Optional[dict]: class AuthenticationMiddleware: """ Pure ASGI middleware that authenticates each request by checking - if the Authorization header exists and equals "Bearer {api_key}". + if the Authorization Bearer token exists and equals anyof "{api_key}". Notes ----- @@ -1263,7 +1265,26 @@ class AuthenticationMiddleware: def __init__(self, app: ASGIApp, tokens: list[str]) -> None: self.app = app - self.api_tokens = {f"Bearer {token}" for token in tokens} + self.api_tokens = [ + hashlib.sha256(t.encode("utf-8")).digest() for t in tokens + ] + + def verify_token(self, headers: Headers) -> bool: + authorization_header_value = headers.get("Authorization") + if not authorization_header_value: + return False + + scheme, _, param = authorization_header_value.partition(" ") + if scheme.lower() != "bearer": + return False + + param_hash = hashlib.sha256(param.encode("utf-8")).digest() + + token_match = False + for token_hash in self.api_tokens: + token_match |= secrets.compare_digest(param_hash, token_hash) + + return token_match def __call__(self, scope: Scope, receive: Receive, send: Send) -> Awaitable[None]: @@ -1276,8 +1297,7 @@ def __call__(self, scope: Scope, receive: Receive, url_path = URL(scope=scope).path.removeprefix(root_path) headers = Headers(scope=scope) # Type narrow to satisfy mypy. - if url_path.startswith("/v1") and headers.get( - "Authorization") not in self.api_tokens: + if url_path.startswith("/v1") and not self.verify_token(headers): response = JSONResponse(content={"error": "Unauthorized"}, status_code=401) return response(scope, receive, send)
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-wr9h-g72x-mwhmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-59425ghsaADVISORY
- github.com/vllm-project/vllm/blob/4b946d693e0af15740e9ca9c0e059d5f333b1083/vllm/entrypoints/openai/api_server.pyghsax_refsource_MISCWEB
- github.com/vllm-project/vllm/commit/ee10d7e6ff5875386c7f136ce8b5f525c8fcef48ghsax_refsource_MISCWEB
- github.com/vllm-project/vllm/releases/tag/v0.11.0ghsax_refsource_MISCWEB
- github.com/vllm-project/vllm/security/advisories/GHSA-wr9h-g72x-mwhmghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.