-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
The AddInsecureBypassPattern method of http.CrossOriginProtection, introduced in version 1.25, shows unexpected behavior.
This method is supposed to allow requests matching a given pattern to bypass protection. The issue is that more requests than expected end up being bypassed.
For example, if you define a ServeMux with two paths, /hello and /hello/:
mux := http.NewServeMux()
mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("/hello"))
})
mux.HandleFunc("/hello/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("/hello/"))
})and you configure the bypass for /hello/:
c := http.NewCrossOriginProtection()
c.AddInsecureBypassPattern("/hello/")
h := c.Handler(mux)the result is that /hello also gets bypassed. Here's a complete example:
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/hello/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("/hello/"))
})
mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("/hello"))
})
c := http.NewCrossOriginProtection()
c.AddInsecureBypassPattern("/hello/")
h := c.Handler(mux)
r := httptest.NewRequest("POST", "http://example.test/hello", nil)
r.Header.Set("Sec-Fetch-Site", "cross-site")
r.Header.Set("Origin", "https://evil.test")
rec := httptest.NewRecorder()
h.ServeHTTP(rec, r)
fmt.Println(rec.Code, rec.Body.String())
}Why this happens
CrossOriginProtection uses an internal ServeMux to check the bypass pattern. For /hello/, ServeMux would internally redirect /hello to /hello/. As a result, the internal check finds a non-empty match and CrossOriginProtection skips validation.
However, CrossOriginProtection does not actually rewrite or redirect the request path; it forwards the original one. Since the downstream mux defines a real handler for /hello, the request is served without protection.