security: Tighten csp connect-src to prevent external websockets (#2705)

This commit is contained in:
Steven Masley
2022-06-29 11:42:17 -05:00
committed by GitHub
parent ea7f9e2d47
commit 889e2e68ea

View File

@ -256,9 +256,8 @@ const (
CSPFrameAncestors = "frame-ancestors"
)
// secureHeaders is only needed for statically served files. We do not need this for api endpoints.
// It adds various headers to enforce browser security features.
func secureHeaders(next http.Handler) http.Handler {
func cspHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Content-Security-Policy disables loading certain content types and can prevent XSS injections.
// This site helps eval your policy for syntax and other common issues: https://csp-evaluator.withgoogle.com/
// If we ever want to render something like a PDF, we need to adjust "object-src"
@ -267,7 +266,7 @@ func secureHeaders(next http.Handler) http.Handler {
cspSrcs := CSPDirectives{
// All omitted fetch csp srcs default to this.
CSPDirectiveDefaultSrc: {"'self'"},
CSPDirectiveConnectSrc: {"'self' ws: wss:"},
CSPDirectiveConnectSrc: {"'self'"},
CSPDirectiveChildSrc: {"'self'"},
CSPDirectiveScriptSrc: {"'self'"},
CSPDirectiveFontSrc: {"'self'"},
@ -281,7 +280,7 @@ func secureHeaders(next http.Handler) http.Handler {
// https: allows loading images from external sources. This is not ideal
// but is required for the templates page that renders readmes.
// We should find a better solution in the future.
CSPDirectiveImgSrc: {"'self' https: https://cdn.coder.com data:"},
CSPDirectiveImgSrc: {"'self' data:"},
CSPDirectiveFormAction: {"'self'"},
CSPDirectiveMediaSrc: {"'self'"},
// Report all violations back to the server to log
@ -293,11 +292,34 @@ func secureHeaders(next http.Handler) http.Handler {
// "require-trusted-types-for" : []string{"'script'"},
}
// This extra connect-src addition is required to support old webkit
// based browsers (Safari).
// See issue: https://github.com/w3c/webappsec-csp/issues/7
// Once webkit browsers support 'self' on connect-src, we can remove this.
// When we remove this, the csp header can be static, as opposed to being
// dynamically generated for each request.
host := r.Host
// It is important r.Host is not an empty string.
if host != "" {
// We can add both ws:// and wss:// as browsers do not let https
// pages to connect to non-tls websocket connections. So this
// supports both http & https webpages.
cspSrcs.Append(CSPDirectiveConnectSrc, fmt.Sprintf("wss://%[1]s ws://%[1]s", host))
}
var csp strings.Builder
for src, vals := range cspSrcs {
_, _ = fmt.Fprintf(&csp, "%s %s; ", src, strings.Join(vals, " "))
}
w.Header().Set("Content-Security-Policy", csp.String())
next.ServeHTTP(w, r)
})
}
// secureHeaders is only needed for statically served files. We do not need this for api endpoints.
// It adds various headers to enforce browser security features.
func secureHeaders(next http.Handler) http.Handler {
// Permissions-Policy can be used to disabled various browser features that we do not use.
// This can prevent an embedded iframe from accessing these features.
// If we support arbitrary iframes such as generic applications, we might need to add permissions
@ -322,15 +344,11 @@ func secureHeaders(next http.Handler) http.Handler {
}, ", ")
return secure.New(secure.Options{
// Set to ContentSecurityPolicyReportOnly for testing, as all errors are printed to the console log
// but are not enforced.
ContentSecurityPolicy: csp.String(),
PermissionsPolicy: permissions,
// Prevent the browser from sending Referer header with requests
ReferrerPolicy: "no-referrer",
}).Handler(next)
}).Handler(cspHeaders(next))
}
// htmlFiles recursively walks the file system passed finding all *.html files.