CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-23996

The Tell-Tale Heartbeat: Timing Side-Channels in FastAPI API Key

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 15, 2026·5 min read·38 visits

Executive Summary (TL;DR)

The `fastapi-api-key` library (< 1.1.0) leaked the validity of API keys via timing differences. Successful verifications returned immediately, while failures slept for a random duration. Attackers could use this asymmetry to enumerate valid key IDs.

A classic implementation of 'security through obscurity' backfires in the python package `fastapi-api-key`. By attempting to slow down brute-force attacks with artificial delays on failures, the library inadvertently created a high-fidelity timing oracle. This allowed attackers to statistically distinguish between valid and invalid API keys based on response latency, turning a defensive measure into an enumeration vector.

The Hook: When defense becomes a liability

In the world of API security, we often obsess over complex cryptographic failures or massive injection flaws. But sometimes, the most elegant vulnerabilities are born from good intentions. Enter fastapi-api-key, a Python library designed to handle API key management for FastAPI applications. It does what it says on the tin: verifies keys, manages scopes, and generally keeps the riff-raff out.

The developers, seemingly aware of brute-force risks, decided to implement a countermeasure. If someone sends an invalid key, the server shouldn't just reject it; it should stall. The logic is sound in principle: make the attacker wait, and you destroy their throughput. It's a tar pit for bots.

But here is the catch: they only applied the tar pit to the failures. If you presented a valid key, the server would enthusiastically usher you in without delay. This created a binary signal: Fast response = Valid Key. Slow response = Invalid Key. It’s like playing poker against an opponent who pauses for exactly 10 seconds every time they have a bad hand, but slaps their cards down instantly when they have a Royal Flush. You don't need to see their cards to know when to fold.

The Flaw: Anatomy of a Timing Oracle

The vulnerability, tracked as CWE-208 (Observable Timing Discrepancy), resided in the verify_key method of the AbstractApiKeyService class. The code logic was deceptively simple: try to verify the key, and if it fails (raises an exception), sleep for a random interval before raising the error.

This is a textbook side-channel. In a perfect world, a secure comparison functions in 'constant time'—meaning it takes the exact same amount of CPU cycles to reject a fake key as it does to accept a real one. By explicitly injecting asyncio.sleep only into the exception handler, the developers introduced a massive temporal disparity.

While the delay was randomized (jittered), the absence of a delay on the success path created a statistical canyon. A valid request might return in 50ms (processing time). An invalid request might return in 250ms (processing time + sleep). Even across a noisy network (the internet), this 200ms gap is a neon sign blinking 'ACCESS GRANTED' to anyone with a stopwatch and a basic understanding of statistics.

The Code: The Smoking Gun

Let's look at the diff. This is where the logic flaw becomes undeniable. In versions prior to 1.1.0, the code looked something like this (simplified for dramatic effect):

# THE VULNERABLE PATTERN
async def verify_key(self, api_key: str, ...):
    try:
        # If this works, we return IMMEDIATELY. Fast.
        return await self._verify_key(api_key, required_scopes)
    except Exception as e:
        # If this fails, we wait. Slow.
        wait = self._system_random.uniform(self.rrd, self.rrd * 2)
        await asyncio.sleep(wait)
        raise e

The fix in version 1.1.0 (Commit 310b2c5c...) was to ensure that everyone waits, regardless of whether they are a VIP or an intruder. It democratizes the suffering.

# THE PATCHED PATTERN
async def verify_key(self, api_key: str, ...):
    try:
        # Verify, but don't return yet
        result = await self._verify_key(api_key, required_scopes)
    except Exception as exc:
        # Wait on failure
        await self._apply_delay()
        raise exc
 
    # Wait on success too! 
    # Now both paths take (Processing + Delay)
    await self._apply_delay()
    return result

By moving the delay logic (now encapsulated in _apply_delay) to execute in both the success path and the exception path, the timing signature is flattened. The server is now equally slow for everyone, destroying the oracle.

The Exploit: Weaponizing Statistics

How does a hacker actually use this? You might think, "The internet is noisy; latency fluctuates." That is true. A single request is unreliable. But we aren't sending a single request. We are sending hundreds.

The Attack Strategy

  1. Baseline: The attacker sends 100 requests with a known invalid key (e.g., AAAAAA). They calculate the average response time. Let's say it's 300ms (network + processing + sleep).
  2. Targeting: The attacker has a list of potential key IDs they want to verify (perhaps leaked from logs or predictable sequence IDs).
  3. Sampling: For each target ID, they send 50 probes.
  4. Analysis:
    • Target A averages 310ms. Result: INVALID.
    • Target B averages 60ms. Result: VALID.

Because the sleep function was using uniform(rrd, rrd * 2), the jitter was substantial, but the minimum sleep time was essentially a floor. The success path would consistently perform under that floor. A standard Z-test or even a simple threshold check would reveal the valid keys with high confidence.

> [!NOTE] > This vulnerability doesn't recover the secret part of the key if the key is a long random string. However, if the system uses KeyID:Secret format, this allows enumeration of valid KeyIDs, drastically reducing the search space for an offline or online brute-force of the secret component.

The Impact: Why should we care?

The CVSS score is a modest 3.7 (Low), mostly because the Attack Complexity is rated High (due to network noise). Don't let that fool you. In a targeted attack against a high-value API, this is a serious leak.

Information Disclosure: Knowing which users or accounts exist on a platform is the first step in a targeted campaign. If I know Company_A_Prod_Key exists, I focus my phishing or brute-force efforts there.

Denial of Wallet: Since the server is forced to sleep on invalid requests, an attacker can flood the server with garbage keys to exhaust thread pools or open connections, leveraging the application's own defense mechanism to cause a Denial of Service (DoS). By fixing the timing leak, the developers also inadvertently solidified the latency penalty for legitimate users, which is a trade-off often required for security.

Official Patches

AthroniaethGitHub Commit Diff

Fix Analysis (1)

Technical Appendix

CVSS Score
3.7/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N
EPSS Probability
0.06%
Top 100% most exploited

Affected Systems

Python applications using fastapi-api-key < 1.1.0FastAPI backends relying on this library for auth

Affected Versions Detail

Product
Affected Versions
Fixed Version
fastapi-api-key
Athroniaeth
< 1.1.01.1.0
AttributeDetail
CWE IDCWE-208
Attack VectorNetwork (Timing Analysis)
CVSS3.7 (Low)
ImpactInformation Disclosure (Key Enumeration)
Exploit StatusConceptual / PoC Possible
Fix Version1.1.0

MITRE ATT&CK Mapping

T1592Gather Victim Host Information
Reconnaissance
T1110Brute Force
Credential Access
CWE-208
Observable Timing Discrepancy

The product performs a comparison or other operation that takes a variable amount of time depending on the value of the input, allowing an attacker to determine information about the input based on the elapsed time.

Known Exploits & Detection

TheoryStatistical timing analysis using repeated sampling of API endpoints.

Vulnerability Timeline

Patch Committed (310b2c5c)
2026-01-20
Version 1.1.0 Released
2026-01-20
GHSA Published
2026-01-21

References & Sources

  • [1]GHSA Advisory
  • [2]NVD Entry

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•1 day ago•CVE-2026-115895
8.8

CVE-2026-115895: Code Injection via Unsanitized Import Alias in Happy DOM ECMAScript Module Compiler

CVE-2026-115895 is a high-severity code injection vulnerability (CWE-94) in the happy-dom npm package prior to version 21.0.4. The vulnerability exists within the ECMAScriptModuleCompiler component, which transforms ECMAScript module syntax into executable Node.js functions when JavaScript evaluation is enabled. Due to an incomplete fix for CVE-2026-33943, the compiler failed to validate local named import aliases against legal JavaScript identifier rules. Attackers supplying crafted module imports—specifically utilizing destructuring default initializers alongside JSON module attributes—can inject arbitrary JavaScript expressions directly into the host Node.js runtime context.

Amit Schendel
Amit Schendel
7 views•10 min read
•1 day ago•CVE-2026-115883
6.0

CVE-2026-115883: Cross-Namespace Access via Dot-Delimited Labels in LangGraph Postgres and SQLite Stores

An improper input validation and authorization bypass vulnerability exists in the LangGraph storage layer affecting langgraph, langgraph-checkpoint-postgres, and langgraph-checkpoint-sqlite. Because relational store implementations serialize namespace tuples into dot-delimited strings without validating labels on read, search, delete, and batch operations, an attacker who controls a namespace label can inject periods (.) to traverse namespace boundaries and access, modify, or delete data belonging to other namespaces.

Amit Schendel
Amit Schendel
8 views•9 min read
•1 day ago•CVE-2026-115886
6.5

CVE-2026-115886: Unauthenticated Server-Side Request Forgery and Unsafe Reflection in MLflow Scorer Evaluation Endpoints

CVE-2026-115886 (GHSA-2wqp-q3c6-ccj5) is a Server-Side Request Forgery (SSRF) and Unsafe Reflection vulnerability in MLflow versions 3.12.0rc0 prior to 3.17.0. Unauthenticated REST API endpoints accept serialized third-party scorer JSON payloads that are dynamically deserialized and reflectively instantiated without sanitizing constructor keyword arguments. When optional dependencies such as trulens and trulens-providers-litellm are installed, attackers can inject egress-steering arguments into the scorer payload, forcing the MLflow server to dispatch outbound HTTP requests to arbitrary internal destinations or cloud metadata services.

Alon Barad
Alon Barad
9 views•10 min read
•1 day ago•CVE-2026-113872
7.5

CVE-2026-113872: Unauthenticated Process Crash in Express Multer via Late Busboy Stream Resumption

CVE-2026-113872 (GHSA-q7qp-rjg7-q26m) is a high-severity denial-of-service vulnerability affecting the multer multipart/form-data upload middleware for Node.js and Express in versions 2.0.0 through 2.4.0. When an endpoint is configured to accept multiple files using an asynchronous fileFilter callback, a crafted, truncated multipart HTTP request can trigger an unhandled error event on the underlying busboy stream parser after request teardown has already completed. Because all error listeners are stripped during teardown, Node.js treats the resulting error event as an uncaught exception and immediately terminates the server process.

Amit Schendel
Amit Schendel
7 views•12 min read
•1 day ago•CVE-2026-115897
2.1

CVE-2026-115897: Cross-Site Scripting via Function Body Boundary Bypass in serialize-javascript

CVE-2026-115897 (also tracked as GHSA-m5mm-p8h7-m29v and aliased as CVE-2026-115754) is a Cross-Site Scripting (XSS) vulnerability in the Yahoo serialize-javascript npm package prior to version 7.1.3. The flaw exists in the regular expression used by escapeFunctionBody() to neutralize HTML script closing tags within serialized JavaScript functions. Because function bodies are sanitized in isolation before structural formatting is applied, a function whose string representation terminates directly with the sequence '</script' bypasses the positive lookahead check. When combined with serialization formatting options that introduce whitespace delimiters and a subsequent property that supplies a closing bracket, an attacker can prematurely terminate an enclosing HTML <script> block and inject arbitrary HTML or JavaScript into the rendered document.

Amit Schendel
Amit Schendel
7 views•13 min read
•1 day ago•CVE-2026-109841
5.9

CVE-2026-109841: Subresource Integrity Verification Bypass in Node.js Undici

CVE-2026-109841 is a Subresource Integrity (SRI) verification bypass vulnerability in undici, the official HTTP/1.1 and HTTP/2 client library for Node.js that powers the global fetch() implementation. The flaw stems from improper handling of case sensitivity (CWE-178) when validating cryptographic hash algorithm tokens within SRI metadata. Because undici compared incoming algorithm identifiers against a strictly lowercase set without prior ASCII-lowercase normalization, valid uppercase or mixed-case tokens such as SHA256- or Sha384- were treated as unrecognized algorithms and discarded. Under SRI forward-compatibility rules, discarding all provided tokens results in an empty integrity list, causing undici to fail open and accept tampered HTTP response bodies without cryptographic verification.

Alon Barad
Alon Barad
10 views•9 min read