Просмотр исходного кода

fix(urllib): reject surrounding whitespace in IsRelativePath

Go's url.Parse does not trim a leading space, so a target like
" //evil.com" parsed with an empty scheme and host and was accepted
as a relative path. Browsers strip leading whitespace before
navigating, turning it into the scheme-relative //evil.com and
enabling an open redirect through the WebAuthn login redirect_url.

Reject any target with leading or trailing whitespace before parsing.
Frédéric Guillot 1 день назад
Родитель
Сommit
b79ba93365
3 измененных файлов с 18 добавлено и 0 удалено
  1. 2 0
      internal/http/response/html_test.go
  2. 8 0
      internal/urllib/url.go
  3. 8 0
      internal/urllib/url_test.go

+ 2 - 0
internal/http/response/html_test.go

@@ -224,7 +224,9 @@ func TestHTMLRedirectRejectsUnsafeTargets(t *testing.T) {
 		"file:///etc/passwd",
 		"mailto:victim@example.org",
 		"//evil.example.org/path",
+		" //evil.example.org/path",
 		`/\evil.example.org/path`,
+		` /\evil.example.org/path`,
 		`\evil.example.org\path`,
 		`/foo\bar`,
 		"ftp://example.org/file",

+ 8 - 0
internal/urllib/url.go

@@ -20,6 +20,14 @@ func IsRelativePath(link string) bool {
 		return false
 	}
 
+	// Reject surrounding whitespace: browsers strip leading and trailing C0
+	// control characters and spaces before parsing a target, while Go's url.Parse
+	// treats spaces as path characters. Without this check, a target like
+	// " //evil.com" is accepted here and becomes //evil.com in the browser.
+	if link != strings.TrimSpace(link) {
+		return false
+	}
+
 	// Reject backslashes: Go's url.Parse treats them as ordinary path
 	// characters, but browsers normalize them to forward slashes, so a target
 	// like "/\evil.com" would parse as relative here yet redirect to

+ 8 - 0
internal/urllib/url_test.go

@@ -22,13 +22,21 @@ func TestIsRelativePath(t *testing.T) {
 		"path?query=value":    true,
 		"path#fragment":       true,
 		"path?query#fragment": true,
+		"/bookmarklet?uri=https://example.org/rss": true,
 
 		// Not relative paths
 		"https://example.org/file.ext": false,
 		"http://example.org/file.ext":  false,
 		"//example.org/file.ext":       false,
 		"//example.org":                false,
+		" //example.org":               false,
+		"  //example.org/path":         false,
+		"\t//example.org":              false,
+		"\n//example.org":              false,
+		" /path":                       false,
+		"/path ":                       false,
 		`/\example.org`:                false,
+		` /\example.org`:               false,
 		`\example.org`:                 false,
 		`path\to\file.ext`:             false,
 		"ftp://example.org/file.ext":   false,