VNX-GO-025 – Potential open redirect via HTTP redirect
Overview
This rule flags instances where HTTP redirect functions (like http.Redirect) use user-controlled input to determine the redirect destination without proper validation. This pattern can lead to open redirect vulnerabilities where attackers can redirect users to malicious websites.
This maps to CWE-601: URL Redirection to Untrusted Site (‘Open Redirect’).
Severity: Medium | CWE: CWE-601 – URL Redirection to Untrusted Site (‘Open Redirect’) | OWASP ASVS: V4.1.2 – Redirect Validation
Why This Matters
Open redirect vulnerabilities occur when an application accepts user-controlled input to determine the destination of a redirect without proper validation. Attackers can craft URLs that appear to link to a trusted site but actually redirect victims to malicious sites for phishing, malware distribution, or credential theft.
These vulnerabilities are often overlooked but can be highly effective in social engineering attacks because the initial URL appears legitimate.
What Gets Flagged
The rule flags HTTP redirect functions that use request parameters, form data, headers, or context values as the redirect destination:
// FLAGGED: Redirect using form data without validation
func loginHandler(w http.ResponseWriter, r *http.Request) {
// ... authentication logic ...
redirectURL := r.FormValue("redirect")
http.Redirect(w, r, redirectURL, http.StatusFound) // User-controlled redirect
}
// FLAGGED: Redirect using query parameters
func oauthCallback(w http.ResponseWriter, r *http.Request) {
state := r.URL.Query().Get("state")
redirectURL := fmt.Sprintf("/app?state=%s", state)
http.Redirect(w, r, redirectURL, http.StatusTemporaryRedirect) // Potential open redirect
}
// FLAGGED: Redirect using header values
func proxyHandler(w http.ResponseWriter, r *http.Request) {
target := r.Header.Get("X-Redirect-URL")
http.Redirect(w, r, target, http.StatusSeeOther) // User-controlled redirect
}
// FLAGGED: Redirect using context values
func apiHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
redirectURL := ctx.Value("redirectURL").(string)
http.Redirect(w, r, redirectURL, http.StatusFound) // From context
}
Remediation
Validate redirect URLs against an allowlist: Only allow redirects to trusted domains or paths:
// SAFE: Validate against allowlist func loginHandler(w http.ResponseWriter, r *http.Request) { redirectURL := r.FormValue("redirect") if isAllowedRedirectURL(redirectURL) { http.Redirect(w, r, redirectURL, http.StatusFound) return } // Default to safe location http.Redirect(w, r, "/", http.StatusFound) } func isAllowedRedirectURL(url string) bool { // Parse URL and check host against allowed domains u, err := url.Parse(url) if err != nil { return false } allowed := map[string]bool{ "example.com": true, "www.example.com": true, } return allowed[u.Host] && u.IsAbs() // Only absolute URLs to allowed domains }Use relative paths for internal redirects: When redirecting within your own site:
// SAFE: Use relative paths that you control func handler(w http.ResponseWriter, r *http.Request) { // Validate it's a relative path without dangerous elements redirectPath := r.FormValue("redirect") if strings.HasPrefix(redirectPath, "/") && !strings.Contains(redirectPath, "..") && !strings.Contains(redirectPath, "//") { http.Redirect(w, r, redirectPath, http.StatusFound) return } http.Error(w, "Invalid redirect path", http.StatusBadRequest) }Implement redirect validation middleware: Create reusable validation logic:
// SAFE: Middleware for redirect validation func redirectValidator(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/redirect" { target := r.URL.Query().Get("url") if !isSafeRedirectTarget(target) { http.Error(w, "Invalid redirect URL", http.StatusBadRequest) return } } next.ServeHTTP(w, r) }) }Use URL parsing and validation: Always parse and validate URLs properly:
// SAFE: Proper URL validation func isSafeRedirectTarget(input string) bool { if input == "" { return false } // Prevent protocol-relative URLs that could be dangerous if strings.HasPrefix(input, "//") { return false } u, err := url.Parse(input) if err != nil { return false } // Allow only relative paths or known safe absolute URLs if !u.IsAbs() { // Relative path - additional validation return strings.HasPrefix(u.Path, "/") && !strings.Contains(u.Path, "..") } // Absolute URL - check against allowlist allowedHosts := map[string]bool{ "example.com": true, "trusted-partner.com": true, } return allowedHosts[u.Hostname()] }