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