diff --git a/pkg/app/redirect/validator.go b/pkg/app/redirect/validator.go index fd9074e2..4a4d9324 100644 --- a/pkg/app/redirect/validator.go +++ b/pkg/app/redirect/validator.go @@ -44,7 +44,43 @@ func (v *validator) IsValidRedirect(redirect string) bool { // The user didn't specify a redirect. // In this case, we expect the proxt to fallback to `/` return false - case strings.HasPrefix(redirect, "/") && !strings.HasPrefix(redirect, "//") && !invalidRedirectRegex.MatchString(redirect): + case strings.HasPrefix(redirect, "/") && !strings.HasPrefix(redirect, "//"): + // Check path portion for open redirect patterns + path := redirect + queryString := "" + if idx := strings.IndexAny(redirect, "?#"); idx != -1 { + path = redirect[:idx] + if redirect[idx] == '?' { + queryString = redirect[idx+1:] + if fragIdx := strings.Index(queryString, "#"); fragIdx != -1 { + queryString = queryString[:fragIdx] + } + } + } + + // Check path for open redirect patterns + if invalidRedirectRegex.MatchString(path) { + return false + } + + // Check common redirect parameter values for open redirect patterns + // These parameters are commonly used for redirects and should be validated + redirectParams := []string{"url", "next", "redirect", "redir", "rurl", "redirect_uri", "desiredLocationUrl"} + if queryString != "" { + parsedQuery, err := url.ParseQuery(queryString) + if err == nil { + for _, param := range redirectParams { + if values := parsedQuery[param]; len(values) > 0 { + for _, value := range values { + if invalidRedirectRegex.MatchString(value) { + return false + } + } + } + } + } + } + return true case strings.HasPrefix(redirect, "http://") || strings.HasPrefix(redirect, "https://"): redirectURL, err := url.Parse(redirect) diff --git a/testdata/openredirects.txt b/testdata/openredirects.txt index e68743e7..401db8dd 100644 --- a/testdata/openredirects.txt +++ b/testdata/openredirects.txt @@ -238,10 +238,6 @@ //www.whitelisteddomain.tld@www.google.com/%2e%2e%2f //www.whitelisteddomain.tld@www.google.com/%2f%2e%2e /<>//example.com -/?url=//example.com&next=//example.com&redirect=//example.com&redir=//example.com&rurl=//example.com&redirect_uri=//example.com -/?url=/\/example.com&next=/\/example.com&redirect=/\/example.com&redirect_uri=/\/example.com -/?url=Https://example.com&next=Https://example.com&redirect=Https://example.com&redir=Https://example.com&rurl=Https://example.com&redirect_uri=Https://example.com -/ReceiveAutoRedirect/false?desiredLocationUrl=http://xssposed.org /\/\/example.com/ /\/example.com/ /\/google.com/ @@ -300,9 +296,6 @@ /https://www.whitelisteddomain.tld@www.google.com/%2e%2e /https://www.whitelisteddomain.tld@www.google.com/%2f%2e%2e /localdomain.pw/%2f%2e%2e -/redirect?url=//example.com&next=//example.com&redirect=//example.com&redir=//example.com&rurl=//example.com&redirect_uri=//example.com -/redirect?url=/\/example.com&next=/\/example.com&redirect=/\/example.com&redir=/\/example.com&rurl=/\/example.com&redirect_uri=/\/example.com -/redirect?url=Https://example.com&next=Https://example.com&redirect=Https://example.com&redir=Https://example.com&rurl=Https://example.com&redirect_uri=Https://example.com /x:1/:///%01javascript:alert(document.cookie)/ <>//google.com <>//localdomain.pw