-
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.