git.iko.soy/typeable/oauth2-proxy
on master help sign in
Merge commit from fork

Signed-off-by: Jan Larwig <jan@larwig.com>

commit5993067505cac4c8e80192787ccd1f4cba05d994 authorJan Larwig <jan@larwig.com> dateSat 8 Nov 2025, 12:42 (1 year ago) parent8782743
3 files changed+55−4
CHANGELOG.md+1 pkg/middleware/headers.go+33−4 pkg/middleware/headers_test.go+21
CHANGELOG.md+1
@@ -12,6 +12,7 @@
1212- [#3244](https://github.com/oauth2-proxy/oauth2-proxy/pull/3244) chore(deps): upgrade to latest go1.25.3 (@tuunit)
1313- [#3238](https://github.com/oauth2-proxy/oauth2-proxy/pull/3238) chore: Replace pkg/clock with narrowly targeted stub clocks (@dsymonds)
1414- [#3237](https://github.com/oauth2-proxy/oauth2-proxy/pull/3237) - feat: add option to use organization id for preferred username in Google Provider (@pixeldrew)
15+- [GHSA-vjrc-mh2v-45x6](https://github.com/oauth2-proxy/oauth2-proxy/security/advisories/GHSA-vjrc-mh2v-45x6) fix: request header smuggling by stripping all normalized header variants (@tuunit)
1516
1617# V7.12.0
1718
pkg/middleware/headers.go+33−4
@@ -25,10 +25,10 @@ func NewRequestHeaderInjector(headers []options.Header) (alice.Constructor, erro
2525}
2626
2727func newStripHeaders(headers []options.Header) alice.Constructor {
28− headersToStrip := []string{}
28+ headersToStrip := []options.Header{}
2929 for _, header := range headers {
3030 if !header.PreserveRequestValue {
31− headersToStrip = append(headersToStrip, header.Name)
31+ headersToStrip = append(headersToStrip, header)
3232 }
3333 }
3434
@@ -50,10 +50,10 @@ func flattenHeaders(headers http.Header) {
5050 }
5151}
5252
53−func stripHeaders(headers []string, next http.Handler) http.Handler {
53+func stripHeaders(headers []options.Header, next http.Handler) http.Handler {
5454 return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
5555 for _, header := range headers {
56− req.Header.Del(header)
56+ stripNormalizedHeader(req, header)
5757 }
5858 next.ServeHTTP(rw, req)
5959 })
@@ -113,3 +113,32 @@ func injectResponseHeaders(injector header.Injector, next http.Handler) http.Han
113113 next.ServeHTTP(rw, req)
114114 })
115115}
116+
117+// normalizeHeaderName normalizes the header name by lowercasing it
118+// and replacing underscores with hyphens.
119+func normalizeHeaderName(headerName string) string {
120+ headerName = strings.ToLower(headerName)
121+ headerName = strings.ReplaceAll(headerName, "_", "-")
122+ return headerName
123+}
124+
125+// stripNormalizedHeader removes any headers from the request that match
126+// the normalized version of the provided header's name.
127+func stripNormalizedHeader(req *http.Request, header options.Header) {
128+ normalizedName := normalizeHeaderName(header.Name)
129+
130+ toBeDeleted := []string{}
131+ for h := range req.Header {
132+ if normalizeHeaderName(h) == normalizedName {
133+ // necessary to avoid modifying the map while iterating
134+ toBeDeleted = append(toBeDeleted, h)
135+ }
136+ }
137+
138+ for _, h := range toBeDeleted {
139+ // necessary because req.Header.Del accesses the map via
140+ // the header's canonicalized name. We need to delete by
141+ // the original name.
142+ delete(req.Header, h)
143+ }
144+}
pkg/middleware/headers_test.go+21
@@ -205,6 +205,27 @@ var _ = Describe("Headers Suite", func() {
205205 expectedHeaders: nil,
206206 expectedErr: "error building request header injector: error building request injector: error building injector for header \"X-Auth-Request-Authorization\": error loading basicAuthPassword: secret source is invalid: exactly one entry required, specify either value, fromEnv or fromFile",
207207 }),
208+ Entry("strips normalized variants before injecting (no preservation)", headersTableInput{
209+ headers: []options.Header{
210+ {
211+ Name: "X-Auth-Request-User",
212+ Values: []options.HeaderValue{
213+ {
214+ ClaimSource: &options.ClaimSource{Claim: "user"},
215+ },
216+ },
217+ },
218+ },
219+ initialHeaders: http.Header{
220+ "X-Auth-Request-User": []string{"old"},
221+ "X-Auth_Request_User": []string{"evil"},
222+ },
223+ session: &sessionsapi.SessionState{User: "user-123"},
224+ expectedHeaders: http.Header{
225+ "X-Auth-Request-User": []string{"user-123"},
226+ },
227+ expectedErr: "",
228+ }),
208229 )
209230
210231 DescribeTable("the response header injector",