Fix open redirect bypass in saferedirect
Relative redirect URLs are now normalized before validation. Paths containing backslashes (including percent-encoded %5c) are rejected, closing a bypass where /../\evil.com passed checks but http.Redirect normalized to /\evil.com. Reported by Fushuling and RacerZ. Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -54,11 +55,12 @@ func (sr *SafeRedirect) Validate(ctx context.Context, redirectURL string) (strin
|
||||
}
|
||||
|
||||
if strings.HasPrefix(redirectURL, "/") {
|
||||
if len(redirectURL) > 1 && (redirectURL[1] == '/' || redirectURL[1] == '\\') {
|
||||
safePath, ok := normalizeRelativePath(redirectURL)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return redirectURL, true
|
||||
return safePath, true
|
||||
}
|
||||
|
||||
parsedURL, err := url.Parse(redirectURL)
|
||||
@@ -93,3 +95,28 @@ func (sr *SafeRedirect) Redirect(w http.ResponseWriter, r *http.Request, redirec
|
||||
safeURL := sr.GetSafeRedirectURL(r.Context(), redirectURL, fallbackURL)
|
||||
http.Redirect(w, r, safeURL, statusCode)
|
||||
}
|
||||
|
||||
func normalizeRelativePath(redirectURL string) (string, bool) {
|
||||
if strings.HasPrefix(redirectURL, "//") {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if strings.Contains(redirectURL, `\`) || strings.Contains(strings.ToLower(redirectURL), "%5c") {
|
||||
return "", false
|
||||
}
|
||||
|
||||
cleaned := path.Clean(redirectURL)
|
||||
if !strings.HasPrefix(cleaned, "/") {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if len(cleaned) > 1 && (cleaned[1] == '/' || cleaned[1] == '\\') {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if strings.Contains(cleaned, `\`) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return cleaned, true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user