internal: Automated internal backport: GHSA-5wcc-hf24-rf5h.sec.patch to authentik-2026.5 (#22296)

Automated internal backport of patch GHSA-5wcc-hf24-rf5h.sec.patch to authentik-2026.5

Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
This commit is contained in:
authentik-automation[bot]
2026-05-12 19:37:21 +02:00
committed by GitHub
parent dfa639365f
commit c8a6665a8f
3 changed files with 33 additions and 74 deletions
@@ -110,17 +110,6 @@ func (a *Application) getTraefikForwardUrl(r *http.Request) (*url.URL, error) {
// getNginxForwardUrl See https://github.com/kubernetes/ingress-nginx/blob/main/rootfs/etc/nginx/template/nginx.tmpl
func (a *Application) getNginxForwardUrl(r *http.Request) (*url.URL, error) {
ou := r.Header.Get("X-Original-URI")
if ou != "" {
// Turn this full URL into a relative URL
u := &url.URL{
Host: "",
Scheme: "",
Path: ou,
}
a.log.WithField("url", u.String()).Info("building forward URL from X-Original-URI")
return u, nil
}
h := r.Header.Get("X-Original-URL")
if len(h) < 1 {
return nil, errors.New("no forward URL found")
@@ -5,10 +5,8 @@ import (
"net/http/httptest"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"goauthentik.io/internal/outpost/proxyv2/constants"
"goauthentik.io/internal/outpost/proxyv2/types"
api "goauthentik.io/packages/client-go"
)
@@ -47,67 +45,6 @@ func TestForwardHandleNginx_Single_Headers(t *testing.T) {
assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect])
}
func TestForwardHandleNginx_Single_URI(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "https://foo.bar/outpost.goauthentik.io/auth/nginx", nil)
req.Header.Set("X-Original-URI", "/app")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
assert.Equal(t, http.StatusUnauthorized, rr.Code)
s, _ := a.sessions.Get(req, a.SessionName())
assert.Equal(t, "/app", s.Values[constants.SessionRedirect])
}
func TestForwardHandleNginx_Single_Claims(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/outpost.goauthentik.io/auth/nginx", nil)
req.Header.Set("X-Original-URI", "/")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
s, _ := a.sessions.Get(req, a.SessionName())
s.ID = uuid.New().String()
s.Options.MaxAge = 86400
s.Values[constants.SessionClaims] = types.Claims{
Sub: "foo",
Proxy: &types.ProxyClaims{
UserAttributes: map[string]any{
"username": "foo",
"password": "bar",
"additionalHeaders": map[string]any{
"foo": "bar",
},
},
},
}
err := a.sessions.Save(req, rr, s)
if err != nil {
panic(err)
}
rr = httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
h := rr.Result().Header
assert.Equal(t, []string{"Basic Zm9vOmJhcg=="}, h["Authorization"])
assert.Equal(t, []string{"bar"}, h["Foo"])
assert.Equal(t, []string{""}, h["User-Agent"])
assert.Equal(t, []string{""}, h["X-Authentik-Email"])
assert.Equal(t, []string{""}, h["X-Authentik-Groups"])
assert.Equal(t, []string{""}, h["X-Authentik-Jwt"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-App"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-Jwks"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-Outpost"])
assert.Equal(t, []string{""}, h["X-Authentik-Name"])
assert.Equal(t, []string{"foo"}, h["X-Authentik-Uid"])
assert.Equal(t, []string{""}, h["X-Authentik-Username"])
}
func TestForwardHandleNginx_Domain_Blank(t *testing.T) {
a := newTestApplication()
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_DOMAIN.Ptr()
@@ -0,0 +1,33 @@
# GHSA-5wcc-hf24-rf5h
_Reported by [@bugbunny-research](https://github.com/bugbunny-research)_
## Unauthenticated Access via Client-Controlled X-Original-URI Header in Nginx Forward-Auth Mode
### Summary
In nginx forward-auth mode, the authentik outpost reads the forwarded request URL from a header that nginx does not set, but that a client can freely inject. By crafting this header to point at an internal outpost path, an unauthenticated attacker can cause the outpost to return HTTP 200 — causing nginx to forward the original request to the protected backend without authentication.
### Patches
authentik 2025.12.5 and 2026.2.3 fix this issue.
### Impact
This vulnerability only affects deployments using authentik's nginx forward-auth integration. Traefik, Caddy, and proxy mode deployments are not affected.
In nginx forward-auth mode, the outpost builds the URL it evaluates from a header that nginx never sets but clients can freely inject. Because nginx's `auth_request` module forwards all client headers to the authentication subrequest, an attacker-supplied value reaches the outpost unmodified.
The outpost unconditionally allows requests whose forwarded URL path begins with `/outpost.goauthentik.io/` (to permit internal endpoints such as the OAuth callback). An attacker can set the injected header to any path under that prefix, causing the outpost to return 200 and nginx to proxy the original request to the backend as if authenticated.
The attack requires only a single HTTP header — no account, session, or prior knowledge of the target application. Any resource behind the nginx gateway is accessible: reads, writes, and deletes depending on what the backend exposes. The CVSS 3.1 score is 10.0 (Critical).
### Workarounds
Operators can mitigate this immediately at the nginx layer by explicitly clearing the `X-Original-URI` header in the nginx location block that proxies traffic to the outpost. This prevents the attacker-supplied value from reaching the outpost regardless of the application-level logic. Refer to the nginx documentation for `proxy_set_header` to clear individual headers.
### For more information
If you have any questions or comments about this advisory:
- Email us at [security@goauthentik.io](mailto:security@goauthentik.io)