fix(oidc): clamp post_login_redirect to CORS allow-list (open-redirect)
Closes the open-redirect risk surfaced by the v0.3.x security audit:
the OIDC callback handler was using oidc_config.post_login_redirect
verbatim as the redirect base on both error and success paths. An
attacker with admin-key compromise (or a misconfigured operator) could
set that field to an external domain and turn /api/auth/oidc/callback
into a phishing redirector ('dialectic login failed → re-enter
password at evil.com/login').
Fix:
- New AuthHandler.safeRedirectBase(raw) validator:
* empty → '/'
* relative path starting with '/' (but not '//') → keep as-is
* absolute URL whose host is in the allow-list → keep as-is
* everything else → '/'
- allow-list sourced from cfg.CORSAllowOrigins (the same set we
already trust for browser CORS), threaded through NewAuthHandler.
- Applied on BOTH the error branch and success branch of Callback.
Also: drop redundant newline on cmd/dialectic-cli usage Fprintln so
go test ./... passes.
No behavior change for happy path: prod's PostLoginRedirect is
'https://dialectic.hangman-lab.top/oidc/callback', which matches the
CORS allow-list ('https://dialectic.hangman-lab.top'), so the
validator returns it unmodified.
This commit is contained in:
@@ -87,7 +87,13 @@ func Mount(cfg *config.Config, db *sqlx.DB, oidcSvc *oidc.Service, version strin
|
||||
// cookie would prevent the browser from sending it back. The
|
||||
// SameSite=Lax + HttpOnly defenses still apply; CF/origin TLS
|
||||
// covers the wire.
|
||||
authH := handlers.NewAuthHandler(oidcSvc, "dialectic_session", false)
|
||||
// Pass the configured CORS allow-list as the open-redirect allowlist
|
||||
// for the OIDC callback — the SPA hosts we already trust for CORS
|
||||
// are by definition the same hosts a legitimate PostLoginRedirect
|
||||
// can target. Anything else (incl. attacker-set values written via
|
||||
// admin cli compromise) gets clamped back to "/" inside the
|
||||
// handler.
|
||||
authH := handlers.NewAuthHandler(oidcSvc, "dialectic_session", false, cfg.CORSAllowOrigins)
|
||||
|
||||
// Routes.
|
||||
r.Route("/api", func(r chi.Router) {
|
||||
|
||||
Reference in New Issue
Block a user