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:
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:
strings.NewReplacer(" ", "_", "-", "_")
So the trusted header and the attacker-controlled alias collide in the backend-visible CGI/PHP namespace.