Caddy: FastCGI header normalization bypass in `forward_auth copy_headers`
Date: 2026-06
Affected: go/github.com/caddyserver/caddy, go/github.com/caddyserver/caddy/v2
Attack vector: auth-bypass
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-52845
CWE: CWE-287
CWE-290
CWE-444
CVSS: 8.1 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N)

### Summary `forward_auth copy_headers` deletes the exact client-supplied identity header before copying the trusted value from the auth gateway. But when the request later goes through `php_fastcgi`, Caddy normalizes HTTP headers into CGI variables by replacing `-` with `_`. This lets a client send an underscore alias that survives the `forward_auth` delete step but becomes the same PHP/FastCGI variable: ```text Remote-Groups -> HTTP_REMOTE_GROUPS Remote_Groups -> HTTP_REMOTE_GROUPS Remote-User -> HTTP_REMOTE_USER Remote_User -> HTTP_REMOTE_USER ``` Result: a remote client can inject or sometimes override identity/group headers trusted by PHP/FastCGI applications behind Caddy. ### Details `forward_auth copy_headers` intentionally removes client-controlled headers before setting values from the auth response: - `modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go:212` - `modules/caddyhttp/reverseproxy/forwardauth/caddyfile.go:222` That delete is exact-field deletion through `http.Header.Del()`: - `modules/caddyhttp/headers/headers.go:255` - `modules/caddyhttp/headers/headers.go:281` So deleting `Remote-Groups` does not delete `Remote_Groups`. Later, FastCGI exports all request headers into CGI variables: - `modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go:410` - `modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go:414` - `modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go:510` The normalizer replaces hyphens with underscores: ```go strings.NewReplacer


Mitigations:

Architectural layers (MAESTRO):