net/http: CrossOriginProtection insecure bypass patterns not limited to exact matches (original) (raw)
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.