RSTR-RDR-003 — Go http.Redirect with request input
Summary
Go counterpart of RSTR-RDR-002. A handler
redirects to a URL read from r.FormValue or r.URL.Query().Get
without checking the host or scheme.
Severity
Medium.
Languages
Go.
What rastray flags
func postLogin(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r,
r.URL.Query().Get("next"), // ← flagged
http.StatusFound)
}
What rastray deliberately does not flag
- Redirects to literal paths.
- Redirects whose target was assembled via
url.URL{Path: ...}from validated components.
How to fix it
Reject absolute URLs and enforce an allow-list:
var allowedHosts = map[string]struct{}{
"app.example.com": {},
}
func safeRedirect(w http.ResponseWriter, r *http.Request, raw string) {
u, err := url.Parse(raw)
if err != nil {
http.Redirect(w, r, "/", http.StatusFound)
return
}
// Relative paths only? Or allow-listed external hosts.
if u.Host == "" || allowedHosts[u.Host] != struct{}{} {
// host empty => relative; or explicitly trusted
} else {
http.Redirect(w, r, "/", http.StatusFound)
return
}
http.Redirect(w, r, raw, http.StatusFound)
}
For most apps the simpler rule — only allow redirects whose target
starts with / and is not // (protocol-relative) — is enough:
target := r.URL.Query().Get("next")
if !strings.HasPrefix(target, "/") || strings.HasPrefix(target, "//") {
target = "/"
}
http.Redirect(w, r, target, http.StatusFound)