You've already forked oauth2-proxy
mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-08-06 22:42:56 +02:00
15
CHANGELOG.md
15
CHANGELOG.md
@ -4,8 +4,22 @@
|
||||
|
||||
## Important Notes
|
||||
|
||||
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.
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
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.
|
||||
|
||||
**Example of affected configuration:**
|
||||
```yaml
|
||||
# This pattern previously matched both:
|
||||
# - /api/foo/status (intended)
|
||||
# - /api/private/sensitive?path=/status (bypass - now fixed)
|
||||
skip_auth_routes: ["^/api/.*/status"]
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
## Changes since v7.10.0
|
||||
|
||||
- [#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 @@
|
||||
- [#3055](https://github.com/oauth2-proxy/oauth2-proxy/pull/3055) feat: support non-default authorization request response mode also for OIDC providers (@stieler-it)
|
||||
- [#3138](https://github.com/oauth2-proxy/oauth2-proxy/pull/3138) feat: make google_groups argument optional when using google provider (@sourava01)
|
||||
- [#3093](https://github.com/oauth2-proxy/oauth2-proxy/pull/3093) feat: differentiate between "no available key" and error for redis sessions (@nobletrout)
|
||||
- [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
|
||||
|
||||
|
||||
# V7.10.0
|
||||
|
@ -580,7 +580,7 @@ func isAllowedMethod(req *http.Request, route allowedRoute) bool {
|
||||
}
|
||||
|
||||
func isAllowedPath(req *http.Request, route allowedRoute) bool {
|
||||
matches := route.pathRegex.MatchString(requestutil.GetRequestURI(req))
|
||||
matches := route.pathRegex.MatchString(requestutil.GetRequestPath(req))
|
||||
|
||||
if route.negate {
|
||||
return !matches
|
||||
|
@ -2,6 +2,8 @@ package util
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
)
|
||||
@ -43,6 +45,25 @@ func GetRequestURI(req *http.Request) string {
|
||||
return uri
|
||||
}
|
||||
|
||||
// GetRequestPath returns the request URI or X-Forwarded-Uri if present and the
|
||||
// request is proxied but always strips the query parameters and only returns
|
||||
// the pure path
|
||||
func GetRequestPath(req *http.Request) string {
|
||||
uri := GetRequestURI(req)
|
||||
|
||||
// Parse URI and return only the path component
|
||||
if parsedURL, err := url.Parse(uri); err == nil {
|
||||
return parsedURL.Path
|
||||
}
|
||||
|
||||
// Fallback: strip query parameters manually
|
||||
if idx := strings.Index(uri, "?"); idx != -1 {
|
||||
return uri[:idx]
|
||||
}
|
||||
|
||||
return uri
|
||||
}
|
||||
|
||||
// IsProxied determines if a request was from a proxy based on the RequestScope
|
||||
// ReverseProxy tracker.
|
||||
func IsProxied(req *http.Request) bool {
|
||||
|
@ -15,14 +15,15 @@ var _ = Describe("Util Suite", func() {
|
||||
const (
|
||||
proto = "http"
|
||||
host = "www.oauth2proxy.test"
|
||||
uri = "/test/endpoint"
|
||||
uriWithQueryParams = "/test/endpoint?query=param"
|
||||
uriNoQueryParams = "/test/endpoint"
|
||||
)
|
||||
var req *http.Request
|
||||
|
||||
BeforeEach(func() {
|
||||
req = httptest.NewRequest(
|
||||
http.MethodGet,
|
||||
fmt.Sprintf("%s://%s%s", proto, host, uri),
|
||||
fmt.Sprintf("%s://%s%s", proto, host, uriWithQueryParams),
|
||||
nil,
|
||||
)
|
||||
})
|
||||
@ -101,13 +102,13 @@ var _ = Describe("Util Suite", func() {
|
||||
req = middleware.AddRequestScope(req, &middleware.RequestScope{})
|
||||
})
|
||||
|
||||
It("returns the URI", func() {
|
||||
Expect(util.GetRequestURI(req)).To(Equal(uri))
|
||||
It("returns the URI (with query params)", func() {
|
||||
Expect(util.GetRequestURI(req)).To(Equal(uriWithQueryParams))
|
||||
})
|
||||
|
||||
It("ignores X-Forwarded-Uri and returns the URI", func() {
|
||||
It("ignores X-Forwarded-Uri and returns the URI (with query params)", func() {
|
||||
req.Header.Add("X-Forwarded-Uri", "/some/other/path")
|
||||
Expect(util.GetRequestURI(req)).To(Equal(uri))
|
||||
Expect(util.GetRequestURI(req)).To(Equal(uriWithQueryParams))
|
||||
})
|
||||
})
|
||||
|
||||
@ -118,13 +119,47 @@ var _ = Describe("Util Suite", func() {
|
||||
})
|
||||
})
|
||||
|
||||
It("returns the URI if X-Forwarded-Uri is not present", func() {
|
||||
Expect(util.GetRequestURI(req)).To(Equal(uri))
|
||||
It("returns the URI if X-Forwarded-Uri is not present (with query params)", func() {
|
||||
Expect(util.GetRequestURI(req)).To(Equal(uriWithQueryParams))
|
||||
})
|
||||
|
||||
It("returns the X-Forwarded-Uri when present", func() {
|
||||
req.Header.Add("X-Forwarded-Uri", "/some/other/path")
|
||||
Expect(util.GetRequestURI(req)).To(Equal("/some/other/path"))
|
||||
It("returns the X-Forwarded-Uri when present (with query params)", func() {
|
||||
req.Header.Add("X-Forwarded-Uri", "/some/other/path?query=param")
|
||||
Expect(util.GetRequestURI(req)).To(Equal("/some/other/path?query=param"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("GetRequestPath", func() {
|
||||
Context("IsProxied is false", func() {
|
||||
BeforeEach(func() {
|
||||
req = middleware.AddRequestScope(req, &middleware.RequestScope{})
|
||||
})
|
||||
|
||||
It("returns the URI (without query params)", func() {
|
||||
Expect(util.GetRequestPath(req)).To(Equal(uriNoQueryParams))
|
||||
})
|
||||
|
||||
It("ignores X-Forwarded-Uri and returns the URI (without query params)", func() {
|
||||
req.Header.Add("X-Forwarded-Uri", "/some/other/path?query=param")
|
||||
Expect(util.GetRequestPath(req)).To(Equal(uriNoQueryParams))
|
||||
})
|
||||
})
|
||||
|
||||
Context("IsProxied is true", func() {
|
||||
BeforeEach(func() {
|
||||
req = middleware.AddRequestScope(req, &middleware.RequestScope{
|
||||
ReverseProxy: true,
|
||||
})
|
||||
})
|
||||
|
||||
It("returns the URI if X-Forwarded-Uri is not present (without query params)", func() {
|
||||
Expect(util.GetRequestPath(req)).To(Equal(uriNoQueryParams))
|
||||
})
|
||||
|
||||
It("returns the X-Forwarded-Uri when present (without query params)", func() {
|
||||
req.Header.Add("X-Forwarded-Uri", "/some/other/path?query=param")
|
||||
Expect(util.GetRequestPath(req)).To(Equal("/some/other/path"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user