fix: CSRF and WS origin check trust request Host header (same-origin)
This commit is contained in:
+34
-15
@@ -26,8 +26,11 @@ var upgrader = websocket.Upgrader{
|
||||
}
|
||||
|
||||
// SetAllowedOrigins replaces the default upgrader with one that validates
|
||||
// the Origin header against a trusted set. Call once at startup.
|
||||
// Exact matches are checked first, then hostname-based matching is used.
|
||||
// the Origin header. Accepts if:
|
||||
// 1. Exact match in origins list, OR
|
||||
// 2. Hostname matches a trusted hostname, OR
|
||||
// 3. Hostname matches the request's own Host header (same-origin), OR
|
||||
// 4. Localhost variant
|
||||
func SetAllowedOrigins(origins []string) {
|
||||
originSet := make(map[string]struct{}, len(origins))
|
||||
hostSet := make(map[string]struct{}, len(origins))
|
||||
@@ -53,34 +56,50 @@ func SetAllowedOrigins(origins []string) {
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
origin := r.Header.Get("Origin")
|
||||
if origin == "" {
|
||||
return true // non-browser clients
|
||||
return true
|
||||
}
|
||||
if _, ok := originSet[origin]; ok {
|
||||
return true
|
||||
}
|
||||
oh := origin
|
||||
if idx := strings.Index(oh, "://"); idx >= 0 {
|
||||
oh = oh[idx+3:]
|
||||
}
|
||||
if idx := strings.Index(oh, "/"); idx >= 0 {
|
||||
oh = oh[:idx]
|
||||
}
|
||||
if idx := strings.LastIndex(oh, ":"); idx >= 0 {
|
||||
oh = oh[:idx]
|
||||
}
|
||||
oh = strings.TrimPrefix(oh, "[")
|
||||
oh = strings.TrimSuffix(oh, "]")
|
||||
oh := extractHostname(origin)
|
||||
|
||||
// Match trusted hostnames
|
||||
if _, ok := hostSet[oh]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// Match request's own Host header (same-origin)
|
||||
reqHost := extractHostname(r.Host)
|
||||
if reqHost != "" && oh == reqHost {
|
||||
return true
|
||||
}
|
||||
|
||||
// Localhost variants
|
||||
if oh == "localhost" || oh == "127.0.0.1" || oh == "::1" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func extractHostname(s string) string {
|
||||
h := s
|
||||
if idx := strings.Index(h, "://"); idx >= 0 {
|
||||
h = h[idx+3:]
|
||||
}
|
||||
if idx := strings.Index(h, "/"); idx >= 0 {
|
||||
h = h[:idx]
|
||||
}
|
||||
if idx := strings.LastIndex(h, ":"); idx >= 0 {
|
||||
h = h[:idx]
|
||||
}
|
||||
h = strings.TrimPrefix(h, "[")
|
||||
h = strings.TrimSuffix(h, "]")
|
||||
return h
|
||||
}
|
||||
|
||||
// Client is a middleman between the websocket connection and the hub.
|
||||
type Client struct {
|
||||
Hub *Hub
|
||||
|
||||
Reference in New Issue
Block a user