mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
net/http: make the zero value of CrossOriginProtection work
Currently, CrossOriginProtection must be constructed by NewCrossOriginProtection. If you try to use the zero value, most methods will panic with a nil dereference. This CL makes CrossOriginProtection use on-demand initialization instead, so the zero value has the same semantics as the value currently returned by NewCrossOriginProtection. Now, NewCrossOriginProtection just constructs the zero value. We keep NewCrossOriginProtection by analogy to NewServeMux. Updates #73626 Fixes #74089. Change-Id: Ia80183eb6bfdafb0e002271c0b25c2d6230a159a Reviewed-on: https://go-review.googlesource.com/c/go/+/680396 Auto-Submit: Austin Clements <austin@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
parent
a35701b352
commit
1cafdfb63b
1 changed files with 28 additions and 9 deletions
|
|
@ -26,12 +26,15 @@ import (
|
||||||
// Requests without Sec-Fetch-Site or Origin headers are currently assumed to be
|
// Requests without Sec-Fetch-Site or Origin headers are currently assumed to be
|
||||||
// either same-origin or non-browser requests, and are allowed.
|
// either same-origin or non-browser requests, and are allowed.
|
||||||
//
|
//
|
||||||
|
// The zero value of CrossOriginProtection is valid and has no trusted origins
|
||||||
|
// or bypass patterns.
|
||||||
|
//
|
||||||
// [Sec-Fetch-Site]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site
|
// [Sec-Fetch-Site]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site
|
||||||
// [Origin]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
|
// [Origin]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
|
||||||
// [Cross-Site Request Forgery (CSRF)]: https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF
|
// [Cross-Site Request Forgery (CSRF)]: https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF
|
||||||
// [safe methods]: https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP
|
// [safe methods]: https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP
|
||||||
type CrossOriginProtection struct {
|
type CrossOriginProtection struct {
|
||||||
bypass *ServeMux
|
bypass atomic.Pointer[ServeMux]
|
||||||
trustedMu sync.RWMutex
|
trustedMu sync.RWMutex
|
||||||
trusted map[string]bool
|
trusted map[string]bool
|
||||||
deny atomic.Pointer[Handler]
|
deny atomic.Pointer[Handler]
|
||||||
|
|
@ -39,10 +42,7 @@ type CrossOriginProtection struct {
|
||||||
|
|
||||||
// NewCrossOriginProtection returns a new [CrossOriginProtection] value.
|
// NewCrossOriginProtection returns a new [CrossOriginProtection] value.
|
||||||
func NewCrossOriginProtection() *CrossOriginProtection {
|
func NewCrossOriginProtection() *CrossOriginProtection {
|
||||||
return &CrossOriginProtection{
|
return &CrossOriginProtection{}
|
||||||
bypass: NewServeMux(),
|
|
||||||
trusted: make(map[string]bool),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTrustedOrigin allows all requests with an [Origin] header
|
// AddTrustedOrigin allows all requests with an [Origin] header
|
||||||
|
|
@ -70,6 +70,9 @@ func (c *CrossOriginProtection) AddTrustedOrigin(origin string) error {
|
||||||
}
|
}
|
||||||
c.trustedMu.Lock()
|
c.trustedMu.Lock()
|
||||||
defer c.trustedMu.Unlock()
|
defer c.trustedMu.Unlock()
|
||||||
|
if c.trusted == nil {
|
||||||
|
c.trusted = make(map[string]bool)
|
||||||
|
}
|
||||||
c.trusted[origin] = true
|
c.trusted[origin] = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +85,21 @@ var noopHandler = HandlerFunc(func(w ResponseWriter, r *Request) {})
|
||||||
// AddInsecureBypassPattern can be called concurrently with other methods
|
// AddInsecureBypassPattern can be called concurrently with other methods
|
||||||
// or request handling, and applies to future requests.
|
// or request handling, and applies to future requests.
|
||||||
func (c *CrossOriginProtection) AddInsecureBypassPattern(pattern string) {
|
func (c *CrossOriginProtection) AddInsecureBypassPattern(pattern string) {
|
||||||
c.bypass.Handle(pattern, noopHandler)
|
var bypass *ServeMux
|
||||||
|
|
||||||
|
// Lazily initialize c.bypass
|
||||||
|
for {
|
||||||
|
bypass = c.bypass.Load()
|
||||||
|
if bypass != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
bypass = NewServeMux()
|
||||||
|
if c.bypass.CompareAndSwap(nil, bypass) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bypass.Handle(pattern, noopHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDenyHandler sets a handler to invoke when a request is rejected.
|
// SetDenyHandler sets a handler to invoke when a request is rejected.
|
||||||
|
|
@ -149,9 +166,11 @@ func (c *CrossOriginProtection) Check(req *Request) error {
|
||||||
// isRequestExempt checks the bypasses which require taking a lock, and should
|
// isRequestExempt checks the bypasses which require taking a lock, and should
|
||||||
// be deferred until the last moment.
|
// be deferred until the last moment.
|
||||||
func (c *CrossOriginProtection) isRequestExempt(req *Request) bool {
|
func (c *CrossOriginProtection) isRequestExempt(req *Request) bool {
|
||||||
if _, pattern := c.bypass.Handler(req); pattern != "" {
|
if bypass := c.bypass.Load(); bypass != nil {
|
||||||
// The request matches a bypass pattern.
|
if _, pattern := bypass.Handler(req); pattern != "" {
|
||||||
return true
|
// The request matches a bypass pattern.
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.trustedMu.RLock()
|
c.trustedMu.RLock()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue