highCVE-2026-54763Aug 6, 2026

CVE-2026-54763: Traefik BasicAuth/DigestAuth/ForwardAuth Underscore Header Identity Spoofing

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Attackers can inject an underscore-variant HTTP header (e.g. X_Auth_User) that bypasses Traefik's identity-header stripping and reaches the backend with a spoofed user value, defeating BasicAuth…

Packagegithub.com/traefik/traefik/v2
Ecosystemgo
Affected<= 2.11.50
Fixed in2.11.51
CVE-2026-54763: Traefik BasicAuth/DigestAuth/ForwardAuth Underscore Header Identity Spoofing

The problem

Traefik's BasicAuth and DigestAuth middlewares call req.Header.Del(headerField) before writing the authenticated username. Go's textproto.CanonicalMIMEHeaderKey normalises ASCII case and treats - as a word separator, but it does NOT treat _ as a separator.

So Del("X-Auth-User") leaves an attacker-supplied X_Auth_User header completely untouched.

The surviving header is then forwarded to the backend alongside Traefik's own writeback. Backends that normalise _ and - equivalently (CGI/WSGI per RFC 3875, PHP $_SERVER, nginx with underscores_in_headers on, Tomcat, ASGI frameworks) will merge or prefer the attacker's value.

The ForwardAuth authResponseHeaders path is worse: the attacker does not need credentials at all, because the underscore header is injected before authentication runs.

Proof of concept

A working proof-of-concept for CVE-2026-54763 in github.com/traefik/traefik/v2, with the exact payload below.

bash
# BasicAuth bypass: valid credentials + underscore-variant spoof header
# Backend receives both x-auth-user: alice (Traefik) AND x_auth_user: superadmin (attacker)
curl -s -u alice:secret123 \
  -H 'X_Auth_User: superadmin' \
  http://traefik-host/protected

# Double-send (canonical stripped, underscore survives)
curl -s -u alice:secret123 \
  -H 'X-Auth-User: superadmin' \
  -H 'X_Auth_User: superadmin' \
  http://traefik-host/protected

# ForwardAuth path: NO credentials needed
curl -s \
  -H 'X_Auth_User: superadmin' \
  http://traefik-host/forward-auth-protected

The root cause is the incomplete fix from CVE-2026-33433. That fix added req.Header.Del(b.headerField) in pkg/middlewares/auth/basic_auth.go and digest_auth.go, but Go's Del canonicalises via textproto.CanonicalMIMEHeaderKey, which maps - to a word-boundary marker and normalises case, but treats _ as a regular letter.

A key like X_Auth_User therefore has a different canonical form from X-Auth-User and survives the Del call untouched.

The same gap exists in pkg/middlewares/auth/forward.go and pkg/middlewares/ingressnginx/snippet/snippet.go for the authResponseHeaders per-name writeback. Traefik's fast proxy (pkg/proxy/fast/proxy.go) explicitly calls DisableNormalizing() on the outgoing fasthttp request, guaranteeing the underscore header reaches the backend wire verbatim.

The patch introduces a new allowHeadersWithUnderscores: false entry-point option (PR #13262, commit 108a5264) that strips all headers containing underscores at the entry point, before any middleware executes.

The fix

Upgrade to Traefik v2.11.51, v3.6.22, or v3.7.6. After upgrading, set allowHeadersWithUnderscores: false on every entry point that fronts a route protected by BasicAuth, DigestAuth, or ForwardAuth. This option, added in PR #13262, strips all underscore-name headers at ingress before middleware processing begins.

Without explicitly setting this option the upgrade alone does not close the bypass.

Reported by Matteo Panzeri.

References: [1][2][3][4][5][6]

Related research