Skip to content

Commit d7258f7

Browse files
author
Max Schaefer
committed
Go: Improve QHelp for go/unvalidated-url-redirection.
The example showed a different (and better) fix from what the help claimed, but the suggestion also had a subtle bug that I fixed at the same time.
1 parent a950de3 commit d7258f7

File tree

3 files changed

+31
-10
lines changed

3 files changed

+31
-10
lines changed

go/ql/src/Security/CWE-601/OpenUrlRedirect.qhelp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ To guard against untrusted URL redirection, it is advisable to avoid putting use
1616
a redirect URL. Instead, maintain a list of authorized redirects on the server; then choose from
1717
that list based on the user input provided.
1818
</p>
19+
<p>
20+
If this is not possible, then the user input should be validated in some other way,
21+
for example, by verifying that the target URL is local and does not redirect to a different host.
22+
</p>
1923
</recommendation>
2024

2125
<example>
@@ -27,11 +31,17 @@ validating the input, which facilitates phishing attacks:
2731
<sample src="OpenUrlRedirect.go"/>
2832

2933
<p>
30-
One way to remedy the problem is to validate the user input against a known fixed string
31-
before doing the redirection:
34+
One way to remedy the problem is to parse the target URL and check that its hostname is empty,
35+
which means that it is a relative URL:
3236
</p>
3337

3438
<sample src="OpenUrlRedirectGood.go"/>
39+
40+
<p>
41+
Note that some browsers treat backslashes in URLs as forward slashes. To account for this,
42+
we replace all backslashes with forward slashes before parsing the URL and checking its hostname.
43+
</p>
44+
3545
</example>
3646

3747
<references>

go/ql/src/Security/CWE-601/OpenUrlRedirectGood.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,26 @@ package main
33
import (
44
"net/http"
55
"net/url"
6+
"strings"
67
)
78

8-
func serve() {
9+
func serve1() {
910
http.HandleFunc("/redir", func(w http.ResponseWriter, r *http.Request) {
1011
r.ParseForm()
11-
target, err := url.Parse(r.Form.Get("target"))
12+
targetUrl := r.Form.Get("target")
13+
// replace all backslashes with forward slashes before parsing the URL
14+
targetUrl = strings.ReplaceAll(targetUrl, "\\", "/")
15+
16+
target, err := url.Parse(targetUrl)
1217
if err != nil {
1318
// ...
1419
}
1520

16-
if target.Hostname() == "semmle.com" {
17-
// GOOD: checking hostname
21+
if target.Hostname() == "" {
22+
// GOOD: check that it is a local redirect
1823
http.Redirect(w, r, target.String(), 302)
1924
} else {
20-
http.WriteHeader(400)
25+
w.WriteHeader(400)
2126
}
2227
})
2328
}

go/ql/test/query-tests/Security/CWE-601/OpenUrlRedirect/OpenUrlRedirectGood.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,23 @@ package main
33
import (
44
"net/http"
55
"net/url"
6+
"strings"
67
)
78

89
func serve1() {
910
http.HandleFunc("/redir", func(w http.ResponseWriter, r *http.Request) {
1011
r.ParseForm()
11-
target, err := url.Parse(r.Form.Get("target"))
12+
targetUrl := r.Form.Get("target")
13+
// replace all backslashes with forward slashes before parsing the URL
14+
targetUrl = strings.ReplaceAll(targetUrl, "\\", "/")
15+
16+
target, err := url.Parse(targetUrl)
1217
if err != nil {
1318
// ...
1419
}
15-
if target.Hostname() == "semmle.com" {
16-
// GOOD: checking hostname
20+
21+
if target.Hostname() == "" {
22+
// GOOD: check that it is a local redirect
1723
http.Redirect(w, r, target.String(), 302)
1824
} else {
1925
w.WriteHeader(400)

0 commit comments

Comments
 (0)