forwardauth: Skip copying missing response headers#6608
Merged
Conversation
6afa8a2 to
8bce42e
Compare
8bce42e to
222a84f
Compare
mholt
approved these changes
Nov 4, 2024
Member
mholt
left a comment
There was a problem hiding this comment.
Cool, thanks for maintaining this!
1 task
mohammed90
pushed a commit
to cedricziel/caddy
that referenced
this pull request
Aug 29, 2025
NucleiAv
added a commit
to NucleiAv/caddy
that referenced
this pull request
Mar 4, 2026
…lied headers When using copy_headers in a forward_auth block, client-supplied headers with the same names were not being removed before being forwarded to the backend. This happens because PR caddyserver#6608 added a MatchNot guard that skips the Set operation when the auth service does not return a given header. That guard prevents setting headers to empty strings, which is the correct behavior, but it also means a client can send X-User-Id: admin in their request and if the auth service validates the token without returning X-User-Id, Caddy skips the Set and the client value passes through unchanged to the backend. The fix adds an unconditional delete route for each copy_headers entry, placed just before the existing conditional set route. The delete always runs regardless of what the auth service returns. The conditional set still only runs when the auth service provides that header. The end result is: - Client-supplied headers are always removed - When the auth service returns the header, the backend gets that value - When the auth service does not return the header, the backend sees nothing Existing behavior is unchanged for any deployment where the auth service returns all of the configured copy_headers entries. Fixes GHSA-7r4p-vjf4-gxv4
NucleiAv
added a commit
to NucleiAv/caddy
that referenced
this pull request
Mar 4, 2026
…lied headers When using copy_headers in a forward_auth block, client-supplied headers with the same names were not being removed before being forwarded to the backend. This happens because PR caddyserver#6608 added a MatchNot guard that skips the Set operation when the auth service does not return a given header. That guard prevents setting headers to empty strings, which is the correct behavior, but it also means a client can send X-User-Id: admin in their request and if the auth service validates the token without returning X-User-Id, Caddy skips the Set and the client value passes through unchanged to the backend. The fix adds an unconditional delete route for each copy_headers entry, placed just before the existing conditional set route. The delete always runs regardless of what the auth service returns. The conditional set still only runs when the auth service provides that header. The end result is: - Client-supplied headers are always removed - When the auth service returns the header, the backend gets that value - When the auth service does not return the header, the backend sees nothing Existing behavior is unchanged for any deployment where the auth service returns all of the configured copy_headers entries. Fixes GHSA-7r4p-vjf4-gxv4
francislavoie
pushed a commit
that referenced
this pull request
Mar 4, 2026
…headers (Fixes GHSA-7r4p-vjf4-gxv4) (#7545) When using copy_headers in a forward_auth block, client-supplied headers with the same names were not being removed before being forwarded to the backend. This happens because PR #6608 added a MatchNot guard that skips the Set operation when the auth service does not return a given header. That guard prevents setting headers to empty strings, which is the correct behavior, but it also means a client can send X-User-Id: admin in their request and if the auth service validates the token without returning X-User-Id, Caddy skips the Set and the client value passes through unchanged to the backend. The fix adds an unconditional delete route for each copy_headers entry, placed just before the existing conditional set route. The delete always runs regardless of what the auth service returns. The conditional set still only runs when the auth service provides that header. The end result is: - Client-supplied headers are always removed - When the auth service returns the header, the backend gets that value - When the auth service does not return the header, the backend sees nothing Existing behavior is unchanged for any deployment where the auth service returns all of the configured copy_headers entries. Fixes GHSA-7r4p-vjf4-gxv4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reported here: https://caddy.community/t/copy-header-copying-empty-headers-with-authelia/25885, but I think it has been mentioned elsewhere before as well. Fixes #6610
Basically, because we use
ReplaceKnownin therequest_headerhandler for placeholders, thecopy_headershortcut forforward_authwould cause the request headers to be set to the placeholder string itself instead of skipping, when the response was missing that header.The fix is to use a
not varsmatcher to check that the response header placeholder has a non-empty value first.Unfortunately this produces some super verbose JSON config, but the behaviour will be more correct.