fix(lib): close open redirect when in subrequest mode (#1222)
* test(nginx-external-auth): bring up to code standards Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(lib): close open redirect when in subrequest mode Closes GHSA-cf57-c578-7jvv Previously Anubis had an open redirect in subrequest auth mode due to an insufficent fix in GHSA-jhjj-2g64-px7c. This patch adds additional validation at several steps of the flow to prevent open redirects in subrequest auth mode as well as implements automated testing to prevent this from occuring in the future. * docs: update CHANGELOG Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
parent
3dab060bfa
commit
7ed1753fcc
8 changed files with 384 additions and 64 deletions
31
lib/http.go
31
lib/http.go
|
|
@ -296,6 +296,16 @@ func (s *Server) constructRedirectURL(r *http.Request) (string, error) {
|
|||
if proto == "" || host == "" || uri == "" {
|
||||
return "", errors.New(localizer.T("missing_required_forwarded_headers"))
|
||||
}
|
||||
|
||||
switch proto {
|
||||
case "http", "https":
|
||||
// allowed
|
||||
default:
|
||||
lg := internal.GetRequestLogger(s.logger, r)
|
||||
lg.Warn("invalid protocol in X-Forwarded-Proto", "proto", proto)
|
||||
return "", errors.New(localizer.T("invalid_redirect"))
|
||||
}
|
||||
|
||||
// Check if host is allowed in RedirectDomains (supports '*' via glob)
|
||||
if len(s.opts.RedirectDomains) > 0 && !matchRedirectDomain(s.opts.RedirectDomains, host) {
|
||||
lg := internal.GetRequestLogger(s.logger, r)
|
||||
|
|
@ -369,16 +379,31 @@ func (s *Server) ServeHTTPNext(w http.ResponseWriter, r *http.Request) {
|
|||
localizer := localization.GetLocalizer(r)
|
||||
|
||||
redir := r.FormValue("redir")
|
||||
urlParsed, err := r.URL.Parse(redir)
|
||||
urlParsed, err := url.ParseRequestURI(redir)
|
||||
if err != nil {
|
||||
s.respondWithStatus(w, r, localizer.T("redirect_not_parseable"), makeCode(err), http.StatusBadRequest)
|
||||
// if ParseRequestURI fails, try as relative URL
|
||||
urlParsed, err = r.URL.Parse(redir)
|
||||
if err != nil {
|
||||
s.respondWithStatus(w, r, localizer.T("redirect_not_parseable"), makeCode(err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// validate URL scheme to prevent javascript:, data:, file:, tel:, etc.
|
||||
switch urlParsed.Scheme {
|
||||
case "", "http", "https":
|
||||
// allowed: empty scheme means relative URL
|
||||
default:
|
||||
lg := internal.GetRequestLogger(s.logger, r)
|
||||
lg.Warn("XSS attempt blocked, invalid redirect scheme", "scheme", urlParsed.Scheme, "redir", redir)
|
||||
s.respondWithStatus(w, r, localizer.T("invalid_redirect"), "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
hostNotAllowed := len(urlParsed.Host) > 0 &&
|
||||
len(s.opts.RedirectDomains) != 0 &&
|
||||
!matchRedirectDomain(s.opts.RedirectDomains, urlParsed.Host)
|
||||
hostMismatch := r.URL.Host != "" && urlParsed.Host != r.URL.Host
|
||||
hostMismatch := r.URL.Host != "" && urlParsed.Host != "" && urlParsed.Host != r.URL.Host
|
||||
|
||||
if hostNotAllowed || hostMismatch {
|
||||
lg := internal.GetRequestLogger(s.logger, r)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue