CVEReports
Reports
CVEReports

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

Product

  • Home
  • Reports
  • Sitemap

Company

  • About
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Powered by Google Gemini & CVE Feed

|
•

CVE-2025-69211
CVSS 6.9|EPSS 0.14%

The Invisible Path: Bypassing NestJS Middleware with URL Encoding

Alon Barad
Alon Barad
Software Engineer•December 30, 2025•5 min read
PoC AvailableNot in KEV

Executive Summary (TL;DR)

If you are using NestJS with Fastify, your middleware might be blind. An attacker can access protected routes like `/admin` by requesting `/%61dmin`. The middleware sees a mismatch and ignores it, but the underlying Fastify router decodes it and serves the restricted content. Patch immediately to version 11.1.11.

A critical normalization discrepancy in the NestJS Fastify adapter allows attackers to bypass middleware security checks simply by URL-encoding characters in the request path.

The Hook: The Bouncer That Can't Read Leetspeak

Imagine a nightclub bouncer who has a very specific list of banned guests. He sees "Smith" on the list and stops him. But if "Smith" shows up wearing a fake mustache and calls himself "Sm%69th", the bouncer shrugs and lets him in. Inside the club, the bartender (the application logic) recognizes him immediately as Smith and serves him the drinks he's not supposed to have.

This is essentially what happened in CVE-2025-69211. NestJS, a darling of the Node.js enterprise world, supports multiple HTTP adapters. If you were using the default Express adapter, you were fine. But if you opted for the high-performance Fastify adapter (@nestjs/platform-fastify), you unknowingly introduced a subtle, dangerous gap between your security middleware and your actual application logic.

The vulnerability is a classic "Parser Differential" or Logical Time-of-Check Time-of-Use (TOCTOU). The component checking your credentials sees one thing, but the component serving the data sees another. It's simple, elegant, and devastatingly effective against middleware-based authentication.

The Flaw: A Tale of Two Parsers

The root cause lies in how NestJS handles middleware matching versus how Fastify handles routing. When you define middleware in NestJS, you often restrict it to specific routes using .forRoutes('admin'). You expect this to protect anything under /admin.

Here is where the logic splits:

  1. The Middleware Check: The NestJS middleware engine checks the incoming request URL to see if it matches the configured path. Crucially, in affected versions, it performed this check against the raw, encoded path string.
  2. The Router Dispatch: After the middleware runs (or decides not to run), the request is handed off to Fastify. Fastify, being a robust web framework, automatically performs URL decoding (normalization) before matching the route to a controller.

This created a blind spot. An attacker sends GET /%61dmin. The middleware regex looks at /%61dmin, compares it to /admin, says "Nope, no match here," and steps aside. The request flows through to Fastify. Fastify sees /%61dmin, decodes %61 to a, resolves it to /admin, and executes the sensitive controller code.

The Code: Fixing the Normalization Gap

The fix is technically simple but conceptually vital: Normalize before you validate. The developers had to ensure that the middleware engine sees the same URL that the router will eventually see.

In the commit c4cedda15a05aafec1e6045b36b0335ab850e771, the file packages/platform-fastify/adapters/middie/fastify-middie.ts was updated. The patch introduces explicit decoding using safeDecodeURI before running the path matching logic.

Before (Vulnerable):

// The code implicitly trusted the raw URL
if (regexp.exec(url)) {
  // execute middleware
}

After (Patched):

import { safeDecodeURI } from 'find-my-way'; // Fastify's router lib
 
// ... inside the handler
if (regexp) {
  // Decode URL before matching to prevent bypass
  const decodedUrl = safeDecodeURI(url).path;
  const result = regexp.exec(decodedUrl);
  if (result) {
     // execute middleware
  }
}

By forcing the URL to be decoded before the regex check, the middleware now correctly identifies /%61dmin as /admin, triggering the security checks.

The Exploit: Walking Through Walls

Let's assume a standard NestJS setup where an AuthMiddleware checks for a valid JWT on all routes starting with /users. We want to steal user data, but we don't have a token.

Step 1: Verify the Target We send a normal request: GET /users/101 Response: 401 Unauthorized (The middleware caught us).

Step 2: The Encode Trick We take the first character of the path, u, and convert it to its hex equivalent %75. Request: GET /%75sers/101

Step 3: The Bypass

  • Middleware: Compares /%75sers/101 against /users. No match. Execution proceeds without checking for a token.
  • Fastify: Decodes /%75sers/101 to /users/101. Finds the UsersController.
  • Result: 200 OK. We have the data.

This technique works for any character in the path that doesn't change the semantic meaning when decoded. It effectively renders path-based middleware useless for security boundaries.

The Fix & Mitigation Strategies

The immediate fix is to update the @nestjs/platform-fastify package to version 11.1.11 or later. This includes the logic to decode URLs prior to middleware matching.

However, this vulnerability highlights a broader architectural lesson in NestJS security: Do not rely on Middleware for Authorization.

Why? Middleware in Express/NestJS is a low-level wrapper around the HTTP request. It often runs before the routing context is fully established. In contrast, NestJS Guards run after the router has identified which handler will process the request, but before the handler executes. Guards are context-aware and are bound to the controller method, not a string path regex.

[!TIP] Pro Tip: Migrate your security logic (Authentication/Authorization) to Guards. Guards are not susceptible to this specific URL encoding bypass because they don't rely on string matching the URL path; they rely on the execution context of the class/method being invoked.

Official Patches

NestJSCommit fixing the URL decoding logic in fastify-middie adapter

Fix Analysis (1)

Technical Appendix

CVSS Score
6.9/ 10
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:N
EPSS Probability
0.14%
Top 100% most exploited

Affected Systems

NestJS applications using @nestjs/platform-fastifyNode.js web applications relying on path-based middleware

Affected Versions Detail

ProductAffected VersionsFixed Version
@nestjs/platform-fastify
NestJS
< 11.1.1111.1.11
AttributeDetail
CWE IDCWE-367 (Time-of-Check Time-of-Use)
CVSS Score6.9 (Medium)
Attack VectorNetwork (URL Encoding)
ImpactSecurity Bypass / Authorization Bypass
Affected Component@nestjs/platform-fastify
Fix Version11.1.11

MITRE ATT&CK Mapping

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1027Obfuscated Files or Information
Defense Evasion
CWE-367
Time-of-check Time-of-use (TOCTOU) Race Condition

The product checks the state of a resource before using it, but the resource's state can change between the check and the use in a way that invalidates the results of the check.

Exploit Resources

Known Exploits & Detection

GitHub Commit TestThe patch itself includes a reproduction test case demonstrating the bypass using '%69ncluded'.

Vulnerability Timeline

Vulnerability Timeline

Patch commit pushed to GitHub
2025-02-01
Vulnerability Analysis Published
2025-02-14

References & Sources

  • [1]Patch Commit on GitHub
  • [2]NestJS Security Documentation

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.

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.