feat(daemon): csp nonce handling (#6587)
* Adding nonce handling * Nonce token and fix content length mismatch * Updating nonce injection for override situations * nonce handling in Content Security Policy for custom CSP scenarios * return original length in cspWriter to prevent truncation
This commit is contained in:
1 parent
af937ba6b1
commit
bf24d6607f
4 files changed
+239
-2
No files matched your search
@@ -6,6 +6,8 @@ package controller
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -98,7 +100,8 @@ func (c *Controller) apiHandler(props HandlerProperties) (http.Handler, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
corsWrappedHandler := wrapHandlerWithCors(mux, props)
|
||||
cspWrappedHandler := wrapHandlerWithCsp(mux, props, isUiRequest)
|
||||
corsWrappedHandler := wrapHandlerWithCors(cspWrappedHandler, props)
|
||||
commonWrappedHandler := wrapHandlerWithCommonFuncs(corsWrappedHandler, c, props)
|
||||
callbackInterceptingHandler := wrapHandlerWithCallbackInterceptor(commonWrappedHandler, c)
|
||||
printablePathCheckHandler := cleanhttp.PrintablePathCheckHandler(callbackInterceptingHandler, nil)
|
||||
@@ -759,3 +762,55 @@ func getActions(urlPath string) []string {
|
||||
// Split the rest on ":", returning all actions and sub-actions
|
||||
return strings.Split(rest, ":")
|
||||
}
|
||||
|
||||
func wrapHandlerWithCsp(h http.Handler, props HandlerProperties, isUiRequest func(*http.Request) bool) http.Handler {
|
||||
cspKey := "Content-Security-Policy"
|
||||
defaultCsp := ""
|
||||
if headers, ok := props.ListenerConfig.CustomUiResponseHeaders[0]; ok {
|
||||
if vals := headers[cspKey]; len(vals) > 0 {
|
||||
defaultCsp = vals[0]
|
||||
}
|
||||
// Remove CSP from the map so WrapCustomHeadersHandler does not
|
||||
// overwrite the nonce injected value.
|
||||
delete(headers, cspKey)
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if defaultCsp == "" || !isUiRequest(req) {
|
||||
h.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
// Static assets get the default CSP without a nonce.
|
||||
if strings.LastIndex(req.URL.Path, ".") != -1 {
|
||||
w.Header().Set(cspKey, defaultCsp)
|
||||
h.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate nonce.
|
||||
b := make([]byte, 16)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
w.Header().Set(cspKey, defaultCsp)
|
||||
h.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
nonce := base64.StdEncoding.EncodeToString(b)
|
||||
|
||||
// Inject nonce into the style-src directive of the CSP.
|
||||
nonceToken := fmt.Sprintf("'nonce-%s'", nonce)
|
||||
|
||||
csp := defaultCsp
|
||||
|
||||
if strings.Contains(csp, "style-src ") {
|
||||
csp = strings.Replace(csp, "style-src ", fmt.Sprintf("style-src %s ", nonceToken), 1)
|
||||
} else {
|
||||
csp = fmt.Sprintf("%s; style-src %s 'self'", csp, nonceToken)
|
||||
}
|
||||
|
||||
w.Header().Set(cspKey, csp)
|
||||
// Creating a custom key so we can pull it when serving UI page
|
||||
w.Header().Set("X-Boundary-Csp-Nonce", nonce)
|
||||
h.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user