Home Posts Load Balancer Logic Flaws: Shadow-Path [Deep Dive]
Security Deep-Dive

Load Balancer Logic Flaws: Shadow-Path [Deep Dive]

Load Balancer Logic Flaws: Shadow-Path [Deep Dive]
Dillip Chowdary
Dillip Chowdary
Tech Entrepreneur & Innovator · May 06, 2026 · 11 min read

Bottom Line

If the proxy and the backend interpret the same URL differently, your access control can fail without any broken crypto or memory corruption. CVE-2025-66490 is a clean example of why path parsing is part of the security boundary.

Key Takeaways

  • CVE-2025-66490 affects Traefik path-based routing in versions before 2.11.32 and 3.6.3.
  • The flaw is a split-view bug: Traefik matches one path view while some backends act on another.
  • Encoded reserved characters like %2F can bypass middleware when routers overlap on the same entrypoint.
  • Hardening now depends on explicit encoded-character policy plus keeping sanitizePath enabled.

The most interesting edge security bugs are often logic flaws, not memory corruption. CVE-2025-66490 in Traefik exposed exactly that: a request path could be interpreted one way by the load balancer and another way by the backend, allowing middleware on protected routes to be skipped. There is no official vendor nickname for this issue, but “Shadow-Path” is a useful shorthand for the bug class: a second, more dangerous path interpretation hiding behind the first.

  • Patched versions: v2.11.32 and v3.6.3 in the GitHub advisory.
  • Trigger: path matchers such as PathPrefix, Path, or PathRegex combined with encoded reserved characters.
  • Impact: middleware and routing policy can be bypassed, sending requests to unintended backends.
  • Operator lesson: upgrades alone are not enough if your ingress policy still assumes every component parses URLs identically.

CVE Summary Card

Bottom Line

This was not a classic parser crash. It was a trust-boundary failure: the router, middleware chain, and backend could disagree about what resource the request actually targeted.

  • CVE: CVE-2025-66490
  • Product: Traefik HTTP reverse proxy and load balancer
  • Published: December 8, 2025 in the GitHub advisory and NVD record
  • Affected versions: vendor advisory says <= v2.11.31 and <= v3.6.2; NVD lists fixes at 2.11.32 and 3.6.3
  • Severity: High in the GitHub advisory; CVSS v3.1 6.5 and CVSS v4.0 6.9 in NVD
  • CWE: CWE-436, interpretation conflict
  • Exploit condition: path-based routing plus another router on the same entrypoint plus a backend that decodes reserved characters

Why “Shadow-Path” fits

The public advisory calls this a path normalization bypass. That is accurate, but incomplete for architects. The deeper issue is the existence of two path realities: the one used to decide which middleware runs, and the one the application eventually processes. Security policy was attached to the first path view, while risk lived in the second.

Vulnerable Code Anatomy

The split-view bug class

Traefik had already introduced important path handling changes before the CVE. In v2.11.24 it added sanitizePath, and in v2.11.25 it normalized unreserved characters while intentionally keeping reserved characters encoded during router matching. That behavior tracks RFC 3986 more closely, but it does not guarantee that the backend will make the same choice.

The vulnerable pattern appears when all of the following are true:

  • A protected router matches something like /admin/ and applies auth, block, or rewrite middleware.
  • A broader fallback router matches / on the same host and entrypoint.
  • The incoming request contains an encoded reserved character such as %2F.
  • The backend framework or app stack decodes that reserved character before resource handling.
// Conceptual only: illustrates the logic mismatch, not runnable exploit code.
incomingPath = "/admin%2F"
routerPath = keepReservedCharsEncoded(incomingPath)

if match(PathPrefix("/admin/"), routerPath) {
  apply("security-middleware")
  forward(serviceA)
} else if match(PathPrefix("/"), routerPath) {
  forward(serviceA)
}

backendPath = backendDecodesReservedChars(incomingPath)
// backendPath becomes "/admin/" and reaches sensitive logic

Why normalization alone was not enough

  • Normalization is local. It only helps if every hop uses the same rules and the same timing.
  • Reserved characters are semantic. Decoding %2F can change path structure, not just formatting.
  • Middleware is route-scoped. If the protected router never matches, the security controls attached to it never execute.
  • Fallback routes hide danger. A broad PathPrefix('/') makes the bypass operational instead of theoretical.

This is why the Traefik fix focused on rejecting suspicious encoded characters at the edge rather than trying to outguess every possible backend interpretation.

Attack Timeline

  1. November 2025 groundwork: Traefik’s v2.11.24 and v2.11.25 changes tightened sanitization and normalization, including the decision to keep reserved characters encoded during route matching.
  2. December 4, 2025: PR #12360, titled Reject suspicious encoded characters, was merged into the v2.11 branch. The PR explicitly says it was meant to block “split view scenarios between Traefik and the backends.”
  3. December 4, 2025: Traefik published v2.11.32 release notes listing CVE-2025-66490 and the fix [server] Reject suspicious encoded characters.
  4. December 5, 2025: Traefik’s v3.6.4 public release notes carried the same fix and migration warning, while the advisory identifies v3.6.3 as the patched 3.x baseline.
  5. December 8, 2025: GitHub published advisory GHSA-gm3x-23wp-hc2c, and NVD recorded the CVE the same day.
  6. March 6, 2026: NVD reanalyzed the record and clarified affected CPE ranges for both the 2.11 and 3.x lines.
  7. Later in the 2.11 line: Traefik documentation for v2.11.35 shows the encoded-character options defaulting back to true, shifting responsibility from secure-by-default blocking to explicit operator hardening.

That last point matters. The vendor response evolved from immediate safety toward compatibility, which is reasonable operationally but increases the burden on platform teams to understand their own backend parsing behavior.

Exploitation Walkthrough

Watch out: This section is conceptual only. It describes the failure mode at a systems level and intentionally avoids a working proof of concept.

Common preconditions

  • The deployment uses path-based policy to protect a high-value route such as /admin/, /internal/, or /beta/.
  • A more permissive router still accepts requests for the same host or entrypoint.
  • The application stack or framework decodes reserved characters before dispatch.
  • The security team assumes the load balancer and backend share one canonical path model.

How the bypass unfolds

  1. An attacker identifies a protected path that is guarded by middleware rather than by application-level authorization alone.
  2. The attacker sends a request whose path preserves the protected resource semantically but hides structure with an encoded reserved character, typically a slash-equivalent path transition.
  3. Traefik evaluates router rules against its own path representation, where the protected rule does not match as intended.
  4. The broader router accepts the request and forwards it without the expected middleware chain.
  5. The backend decodes the reserved character, reconstructs the sensitive path shape, and serves logic that should have remained behind the protected route.

The critical mistake is assuming that access control at the proxy remains valid after the application rewrites or reinterprets the target resource. Once those semantics diverge, the edge policy becomes advisory rather than authoritative.

What the real blast radius looks like

  • Admin endpoints can become reachable through the wrong router.
  • Forward-auth chains can be skipped when attached only to the protected path rule.
  • Observability gets messy because logs may show the encoded path at the edge and the decoded path inside the app.
  • Incident response slows down because teams argue about whether the proxy or the app “really” handled the request.

Hardening Guide

The immediate move is to upgrade, but the durable fix is to make path interpretation explicit at every trust boundary. In current Traefik documentation, the encoded-character controls exist per entrypoint and should be treated like security policy, not compatibility toggles.

entryPoints:
  websecure:
    address: ':443'
    http:
      sanitizePath: true
      encodedCharacters:
        allowEncodedSlash: false
        allowEncodedBackSlash: false
        allowEncodedNullCharacter: false
        allowEncodedSemicolon: false
        allowEncodedPercent: false
        allowEncodedQuestionMark: false
        allowEncodedHash: false

If you prefer CLI-based static configuration, the documented pattern is the same, for example --entryPoints.web.http.encodedCharacters.allowEncodedSlash=false.

Practical hardening checklist

  • Upgrade first. Move off versions prior to 2.11.32 and 3.6.3.
  • Keep sanitizePath enabled. Traefik’s docs explicitly warn that setting it to false is not safe.
  • Deny risky encoded characters explicitly. Do not rely on defaults, especially after the later default-value changes in the 2.11 line.
  • Collapse overlapping routers. Avoid pairing high-priority protected prefixes with a broad catch-all route on the same surface unless app auth duplicates the protection.
  • Mirror auth in the application. Proxy middleware should narrow exposure, not be the only control protecting sensitive business logic.
  • Test backend parsing behavior. Frameworks, language runtimes, and upstream app servers do not all treat encoded reserved characters the same way.
  • Diff logs across layers. Compare edge logs, service mesh logs, and app logs for path representation drift.

When you need to share suspicious request samples with teammates or vendors, sanitize them first. A utility like the Data Masking Tool is useful for redacting tokens, user identifiers, and internal path fragments before the examples leave your incident channel.

Pro tip: Add path-canonicalization checks to your ingress conformance suite. Treat encoded slashes, backslashes, semicolons, and hash fragments as regression inputs, not edge cases.

Architectural Lessons

Security boundaries must share one path model

  • Canonicalization is part of authorization. If different layers canonicalize differently, your policy graph is already inconsistent.
  • Standards compliance is not enough by itself. A proxy can be RFC-aligned and still unsafe when paired with a backend that decodes more aggressively.
  • Compatibility defaults are risky. Vendor defaults often move toward least-breakage, not strongest isolation.
  • Path-based controls are brittle. They work best when combined with identity-aware checks inside the service.

What teams should change in design reviews

  • Ask which layer owns the canonical resource identity for an HTTP request.
  • Document whether the backend decodes reserved characters before routing.
  • Require negative tests for encoded path variants whenever a route protects admin, debug, tenant-isolation, or internal APIs.
  • Prefer application authorization tied to resource semantics over proxy-only middleware tied to string patterns.

The larger lesson from CVE-2025-66490 is that modern load balancers are not just transport components. They are parsers, policy engines, and partial security gateways. Once they take on that role, every mismatch between proxy semantics and backend semantics becomes a potential vulnerability surface. Shadow paths are what appear when those semantics drift apart.

Frequently Asked Questions

What is CVE-2025-66490 in Traefik? +
CVE-2025-66490 is a path-normalization bypass in Traefik where requests using encoded reserved characters can evade the middleware chain attached to path-based routers. The issue matters when the proxy and backend disagree about how to interpret the same request path.
Am I affected if my backend never decodes %2F or other reserved characters? +
Your risk is lower, because this bug class depends on a semantic mismatch between layers. If the backend preserves encoded reserved characters exactly as the proxy evaluated them, the original bypass path may not materialize, but you should still upgrade and test rather than assume consistency.
Is Traefik sanitizePath enough to stop this class of bug? +
No. sanitizePath helps with path cleanup such as dot segments and duplicate slashes, and Traefik explicitly warns against disabling it, but it does not solve every reserved-character interpretation mismatch. You also need an explicit encoded-character policy and application-side authorization for sensitive routes.
What should I configure in Traefik after upgrading? +
Keep sanitizePath enabled and set the documented entryPoints.<name>.http.encodedCharacters.* options deliberately for your threat model. If your backend decodes reserved characters, set the relevant allowEncoded* controls to false so the edge rejects ambiguous requests before they hit the app.

Get Engineering Deep-Dives in Your Inbox

Weekly breakdowns of architecture, security, and developer tooling — no fluff.

Found this useful? Share it.