VYPR
High severityNVD Advisory· Published Oct 7, 2025· Updated Oct 7, 2025

vLLM vulnerable to timing attack at bearer auth

CVE-2025-59425

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.

PackageAffected versionsPatched versions
vllmPyPI
< 0.11.00.11.0

Affected products

1

Patches

1
ee10d7e6ff58

Validate API tokens in constant time (#25781)

https://github.com/vllm-project/vllmRussell BryantSep 27, 2025via ghsa
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

News mentions

0

No linked articles in our index yet.