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

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

commit9ffafad4b2d2f9f7668e5504565f356a7c047b77 authorJan Larwig <jan@larwig.com> dateWed 30 Jul 2025, 19:46 (1 year ago) parentf4b33b6
4 files changed+85−14
CHANGELOG.md+15 oauthproxy.go+1−1 pkg/requests/util/util.go+21 pkg/requests/util/util_test.go+48−13
CHANGELOG.md+15
@@ -4,8 +4,22 @@
44
55## Important Notes
66
7+Fixed critical vulnerability where `skip_auth_routes` regex patterns matched against the full request URI (path + query parameters) instead of just the path, allowing authentication bypass attacks.
8+
79## Breaking Changes
810
11+If your configuration relies on matching query parameters in `skip_auth_routes` patterns, you must update your regex patterns to match paths only. Review all `skip_auth_routes` entries for potential impact.
12+
13+**Example of affected configuration:**
14+```yaml
15+# This pattern previously matched both:
16+# - /api/foo/status (intended)
17+# - /api/private/sensitive?path=/status (bypass - now fixed)
18+skip_auth_routes: ["^/api/.*/status"]
19+```
20+
21+For detailed information, migration guidance, and security implications, see the [security advisory](https://github.com/oauth2-proxy/oauth2-proxy/security/advisories/GHSA-7rh7-c77v-6434).
22+
923## Changes since v7.10.0
1024
1125- [#2615](https://github.com/oauth2-proxy/oauth2-proxy/pull/2615) feat(cookies): add option to set a limit on the number of per-request CSRF cookies oauth2-proxy sets (@bh-tt)
@@ -17,6 +31,7 @@
1731- [#3055](https://github.com/oauth2-proxy/oauth2-proxy/pull/3055) feat: support non-default authorization request response mode also for OIDC providers (@stieler-it)
1832- [#3138](https://github.com/oauth2-proxy/oauth2-proxy/pull/3138) feat: make google_groups argument optional when using google provider (@sourava01)
1933- [#3093](https://github.com/oauth2-proxy/oauth2-proxy/pull/3093) feat: differentiate between "no available key" and error for redis sessions (@nobletrout)
34+- [GHSA-7rh7-c77v-6434](https://github.com/oauth2-proxy/oauth2-proxy/security/advisories/GHSA-7rh7-c77v-6434) fix: skip_auth_routes bypass through query parameter inclusion
2035
2136
2237# V7.10.0
oauthproxy.go+1−1
@@ -580,7 +580,7 @@ func isAllowedMethod(req *http.Request, route allowedRoute) bool {
580580}
581581
582582func isAllowedPath(req *http.Request, route allowedRoute) bool {
583− matches := route.pathRegex.MatchString(requestutil.GetRequestURI(req))
583+ matches := route.pathRegex.MatchString(requestutil.GetRequestPath(req))
584584
585585 if route.negate {
586586 return !matches
pkg/requests/util/util.go+21
@@ -2,6 +2,8 @@ package util
22
33import (
44 "net/http"
5+ "net/url"
6+ "strings"
57
68 middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
79)
@@ -43,6 +45,25 @@ func GetRequestURI(req *http.Request) string {
4345 return uri
4446}
4547
48+// GetRequestPath returns the request URI or X-Forwarded-Uri if present and the
49+// request is proxied but always strips the query parameters and only returns
50+// the pure path
51+func GetRequestPath(req *http.Request) string {
52+ uri := GetRequestURI(req)
53+
54+ // Parse URI and return only the path component
55+ if parsedURL, err := url.Parse(uri); err == nil {
56+ return parsedURL.Path
57+ }
58+
59+ // Fallback: strip query parameters manually
60+ if idx := strings.Index(uri, "?"); idx != -1 {
61+ return uri[:idx]
62+ }
63+
64+ return uri
65+}
66+
4667// IsProxied determines if a request was from a proxy based on the RequestScope
4768// ReverseProxy tracker.
4869func IsProxied(req *http.Request) bool {
pkg/requests/util/util_test.go+48−13
@@ -13,16 +13,17 @@ import (
1313
1414var _ = Describe("Util Suite", func() {
1515 const (
16− proto = "http"
17− host = "www.oauth2proxy.test"
18− uri = "/test/endpoint"
16+ proto = "http"
17+ host = "www.oauth2proxy.test"
18+ uriWithQueryParams = "/test/endpoint?query=param"
19+ uriNoQueryParams = "/test/endpoint"
1920 )
2021 var req *http.Request
2122
2223 BeforeEach(func() {
2324 req = httptest.NewRequest(
2425 http.MethodGet,
25− fmt.Sprintf("%s://%s%s", proto, host, uri),
26+ fmt.Sprintf("%s://%s%s", proto, host, uriWithQueryParams),
2627 nil,
2728 )
2829 })
@@ -101,13 +102,13 @@ var _ = Describe("Util Suite", func() {
101102 req = middleware.AddRequestScope(req, &middleware.RequestScope{})
102103 })
103104
104− It("returns the URI", func() {
105− Expect(util.GetRequestURI(req)).To(Equal(uri))
105+ It("returns the URI (with query params)", func() {
106+ Expect(util.GetRequestURI(req)).To(Equal(uriWithQueryParams))
106107 })
107108
108− It("ignores X-Forwarded-Uri and returns the URI", func() {
109+ It("ignores X-Forwarded-Uri and returns the URI (with query params)", func() {
109110 req.Header.Add("X-Forwarded-Uri", "/some/other/path")
110− Expect(util.GetRequestURI(req)).To(Equal(uri))
111+ Expect(util.GetRequestURI(req)).To(Equal(uriWithQueryParams))
111112 })
112113 })
113114
@@ -118,13 +119,47 @@ var _ = Describe("Util Suite", func() {
118119 })
119120 })
120121
121− It("returns the URI if X-Forwarded-Uri is not present", func() {
122− Expect(util.GetRequestURI(req)).To(Equal(uri))
122+ It("returns the URI if X-Forwarded-Uri is not present (with query params)", func() {
123+ Expect(util.GetRequestURI(req)).To(Equal(uriWithQueryParams))
123124 })
124125
125− It("returns the X-Forwarded-Uri when present", func() {
126− req.Header.Add("X-Forwarded-Uri", "/some/other/path")
127− Expect(util.GetRequestURI(req)).To(Equal("/some/other/path"))
126+ It("returns the X-Forwarded-Uri when present (with query params)", func() {
127+ req.Header.Add("X-Forwarded-Uri", "/some/other/path?query=param")
128+ Expect(util.GetRequestURI(req)).To(Equal("/some/other/path?query=param"))
129+ })
130+ })
131+ })
132+
133+ Context("GetRequestPath", func() {
134+ Context("IsProxied is false", func() {
135+ BeforeEach(func() {
136+ req = middleware.AddRequestScope(req, &middleware.RequestScope{})
137+ })
138+
139+ It("returns the URI (without query params)", func() {
140+ Expect(util.GetRequestPath(req)).To(Equal(uriNoQueryParams))
141+ })
142+
143+ It("ignores X-Forwarded-Uri and returns the URI (without query params)", func() {
144+ req.Header.Add("X-Forwarded-Uri", "/some/other/path?query=param")
145+ Expect(util.GetRequestPath(req)).To(Equal(uriNoQueryParams))
146+ })
147+ })
148+
149+ Context("IsProxied is true", func() {
150+ BeforeEach(func() {
151+ req = middleware.AddRequestScope(req, &middleware.RequestScope{
152+ ReverseProxy: true,
153+ })
154+ })
155+
156+ It("returns the URI if X-Forwarded-Uri is not present (without query params)", func() {
157+ Expect(util.GetRequestPath(req)).To(Equal(uriNoQueryParams))
158+ })
159+
160+ It("returns the X-Forwarded-Uri when present (without query params)", func() {
161+ req.Header.Add("X-Forwarded-Uri", "/some/other/path?query=param")
162+ Expect(util.GetRequestPath(req)).To(Equal("/some/other/path"))
128163 })
129164 })
130165 })