Skip to content

Commit

Permalink
Only keep the last incoming header
Browse files Browse the repository at this point in the history
  • Loading branch information
francislavoie committed Mar 6, 2022
1 parent 3830d5d commit e078332
Showing 1 changed file with 28 additions and 13 deletions.
41 changes: 28 additions & 13 deletions modules/caddyhttp/reverseproxy/reverseproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,11 +600,10 @@ func (h Handler) addForwardedHeaders(req *http.Request) error {

// If we aren't the first proxy, and the proxy is trusted,
// retain prior X-Forwarded-For information as a comma+space
// separated list and fold multiple headers into one.
prior, ok := req.Header["X-Forwarded-For"]
omit := ok && prior == nil // Issue 38079: nil now means don't populate the header
if trusted && len(prior) > 0 {
clientIP = strings.Join(prior, ", ") + ", " + clientIP
// separated list (only the last incoming header value is kept).
prior, ok, omit := lastHeaderValue(req.Header, "X-Forwarded-For")
if trusted && ok && prior != "" {
clientIP = prior + ", " + clientIP
}
if !omit {
req.Header.Set("X-Forwarded-For", clientIP)
Expand All @@ -617,10 +616,9 @@ func (h Handler) addForwardedHeaders(req *http.Request) error {
if req.TLS == nil {
proto = "http"
}
prior, ok = req.Header["X-Forwarded-Proto"]
omit = ok && prior == nil
if trusted && len(prior) > 0 {
proto = prior[0]
prior, ok, omit = lastHeaderValue(req.Header, "X-Forwarded-Proto")
if trusted && ok && prior != "" {
proto = prior
}
if !omit {
req.Header.Set("X-Forwarded-Proto", proto)
Expand All @@ -634,10 +632,9 @@ func (h Handler) addForwardedHeaders(req *http.Request) error {
if err != nil {
host = req.Host // OK; there probably was no port
}
prior, ok = req.Header["X-Forwarded-Host"]
omit = ok && prior == nil
if trusted && len(prior) > 0 {
host = prior[0]
prior, ok, omit = lastHeaderValue(req.Header, "X-Forwarded-Host")
if trusted && ok && prior != "" {
host = prior
}
if !omit {
req.Header.Set("X-Forwarded-Host", host)
Expand Down Expand Up @@ -972,6 +969,24 @@ func copyHeader(dst, src http.Header) {
}
}

// lastHeaderValue gets the last value for a given header field
// if more than one is set. If the header field is nil, then
// the omit is true, meaning some earlier logic in the server
// wanted to prevent this header from getting written at all.
// If the header is empty, then ok is false. Callers should
// still check that the value is not empty (the header field
// may be set but have an empty value).
func lastHeaderValue(h http.Header, field string) (value string, ok bool, omit bool) {
values := h.Values(field)
if values == nil {
return "", true, true
}
if len(values) == 0 {
return "", false, false
}
return values[len(values)-1], true, false
}

func upgradeType(h http.Header) string {
if !httpguts.HeaderValuesContainsToken(h["Connection"], "Upgrade") {
return ""
Expand Down

0 comments on commit e078332

Please sign in to comment.