SECURITYCRITICALCVE-2026-14198

CVE-2026-14198: Fastify Middie Middleware Path Bypass (CVSS 9.1)

Critical path bypass vulnerability in @fastify/middie versions 9.1.0 through 9.3.2 allows attackers to evade middleware protection by exploiting a %2F...

Dylan H.

Security Team

July 2, 2026
5 min read
CVE-2026-14198: Fastify Middie Middleware Path Bypass (CVSS 9.1)

Critical severity

Rated critical. Prioritise patching — see the remediation guidance below.

Affected Products

  • @fastify/middie 9.1.0
  • @fastify/middie 9.2.x
  • @fastify/middie 9.3.0
  • @fastify/middie 9.3.1
  • @fastify/middie 9.3.2
  • Node.js applications using Fastify with middie

Advisory Summary

A critical authentication bypass vulnerability (CVSS 9.1) has been assigned to CVE-2026-14198, affecting the @fastify/middie middleware integration package for the Fastify web framework. The flaw allows attackers to bypass middleware-enforced security controls — including authentication, authorization, and rate-limiting — by exploiting a URL path decoding inconsistency between the middleware layer and Fastify's internal router.

AttributeValue
CVE IDCVE-2026-14198
CVSS v3.1 Score9.1 (Critical)
Attack VectorNetwork (AV:N)
Attack ComplexityLow (AC:L)
Privileges RequiredNone (PR:N)
User InteractionNone (UI:N)
Package@fastify/middie
Affected Versions9.1.0 – 9.3.2
Fixed Version9.3.3+
Published2026-07-01

Vulnerability Details

Root Cause: %2F Decoding Mismatch

@fastify/middie decodes the percent-encoded slash %2F inside URL path parameter values before matching middleware paths. Fastify's underlying router, however, preserves the encoding during route lookup. The result is a disagreement between the two layers about what the canonical request path is.

This creates a dangerous inconsistency:

Request URL:  /api/admin%2Fusers

middie sees:  /api/admin/users  → matches middleware path /api/admin/*
                                  → middleware runs (e.g., auth check PASSES)

Fastify sees: /api/admin%2Fusers → does NOT match /api/admin/*
                                    → routes to different handler
                                    → handler runs WITHOUT middleware protection

An attacker who knows or guesses a protected route path can encode a slash as %2F in the URL to slip past middleware while still having their request handled by the target endpoint.

Impact

Any security-critical middleware registered with @fastify/middie is affected:

  • Authentication middleware — unauthenticated requests reach protected endpoints
  • Authorization middleware — role/permission checks bypassed entirely
  • Rate limiting middleware — unauthenticated users bypass request throttling
  • Input validation middleware — pre-processing skipped
  • Audit/logging middleware — requests go unrecorded

Affected Configurations

Applications are vulnerable if all three conditions are met:

  1. Running @fastify/middie version 9.1.0 through 9.3.2
  2. Registering middleware that enforces security on path patterns (e.g., /api/*, /admin/*)
  3. Handling routes whose paths contain parameters that could be expressed with a slash

Common vulnerable patterns:

// Vulnerable: auth middleware on /api/* can be bypassed
fastify.register(require('@fastify/middie'))
 
fastify.addHook('onRequest', async (req, reply) => {
  // This auth check CAN BE BYPASSED via %2F encoding
  if (req.url.startsWith('/api/')) {
    await requireAuth(req, reply)
  }
})
 
fastify.get('/api/users/:id', handler)
// Accessible unauthenticated via: GET /api/users%2F123

Detection

Check Your Package Version

# Check installed version
npm list @fastify/middie
 
# Check package.json for affected version ranges
grep -r "@fastify/middie" package.json package-lock.json

Log Review

Look for requests containing %2F in paths that should be protected:

# Nginx / reverse proxy logs
grep '%2F' /var/log/nginx/access.log | grep -E "/(api|admin|auth)/"
 
# Node.js application logs — check for unauthenticated access patterns
grep -E "403|401|bypass|unauthenticated" app.log

Test for Vulnerability (Safe Testing Only)

# If /api/protected requires auth, this SHOULD return 401:
curl -v http://localhost:3000/api/protected
 
# On a VULNERABLE system, this may return 200 (bypass):
curl -v http://localhost:3000/api%2Fprotected
 
# Or with encoded path separators:
curl -v "http://localhost:3000/api/admin%2Fdashboard"

Remediation

Immediate Fix: Upgrade @fastify/middie

# Upgrade to the patched version
npm install @fastify/middie@latest
 
# Verify
npm list @fastify/middie
# Should show 9.3.3 or higher

Workaround: Explicit Route-Level Hooks

If immediate upgrade is not possible, move security middleware from path-pattern matching to explicit route-level onRequest hooks:

// SAFER: Per-route hook — not affected by path decoding issues
fastify.get('/api/users/:id', {
  onRequest: [requireAuth],  // Auth enforced at route level
  handler: async (req, reply) => {
    return getUser(req.params.id)
  }
})
 
// Or register preHandler hooks per route
fastify.addHook('preHandler', async (req, reply) => {
  // Only runs after routing — path is already resolved
  if (req.routerPath?.startsWith('/api/')) {
    await requireAuth(req, reply)
  }
})

Note: Use req.routerPath (the matched route pattern) rather than req.url (the raw decoded URL) for security checks to avoid decoding ambiguities.

Defense in Depth

Even after patching, apply these hardening measures:

  1. Normalize URLs at the reverse proxy — configure Nginx/Caddy to decode and re-encode URLs before forwarding:

    # Normalize percent-encoded slashes
    location ~ %2F {
      return 400;
    }
  2. Reject %2F at the application boundary — if your application does not legitimately use encoded slashes in paths, block them explicitly.

  3. Add integration tests for auth bypass — test that %2F-encoded variants of protected URLs return 401/403.


Patch Verification

After upgrading, verify the fix is in place:

# Check version
node -e "console.log(require('@fastify/middie/package.json').version)"
 
# Run a quick functional test
# 1. Start your server
# 2. Send a request with %2F to a protected route
# 3. Confirm it returns 401/403, not 200
curl -v "http://localhost:3000/api/admin%2Fdashboard"
# Expected: HTTP/1.1 401 Unauthorized (or 403 Forbidden)

References