From 96be4aa138f9856d667e900e623a3b4038d4c69f Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 28 Jun 2022 08:47:17 -0500 Subject: [PATCH 1/3] security: Tighten csp connect-src to prevent external websockets --- site/site.go | 106 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 44 deletions(-) diff --git a/site/site.go b/site/site.go index f838e2b61645e..d4d303c0c1be1 100644 --- a/site/site.go +++ b/site/site.go @@ -256,48 +256,70 @@ const ( CSPFrameAncestors = "frame-ancestors" ) +// cspHeaders adds +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" + // + // The list of CSP options: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src + cspSrcs := CSPDirectives{ + // All omitted fetch csp srcs default to this. + CSPDirectiveDefaultSrc: {"'self'"}, + CSPDirectiveConnectSrc: {"'self'"}, + CSPDirectiveChildSrc: {"'self'"}, + CSPDirectiveScriptSrc: {"'self'"}, + CSPDirectiveFontSrc: {"'self'"}, + CSPDirectiveStyleSrc: {"'self' 'unsafe-inline'"}, + // object-src is needed to support code-server + CSPDirectiveObjectSrc: {"'self'"}, + // blob: for loading the pwa manifest for code-server + CSPDirectiveManifestSrc: {"'self' blob:"}, + CSPDirectiveFrameSrc: {"'self'"}, + // data: for loading base64 encoded icons for generic applications. + CSPDirectiveImgSrc: {"'self' https://cdn.coder.com data:"}, + CSPDirectiveFormAction: {"'self'"}, + CSPDirectiveMediaSrc: {"'self'"}, + // Report all violations back to the server to log + CSPDirectiveReportURI: {"/api/private/csp/reports"}, + CSPFrameAncestors: {"'none'"}, + + // Only scripts can manipulate the dom. This prevents someone from + // naming themselves something like ''. + // TODO: @emyrk we need to make FE changes to enable this. We get 'TrustedHTML' and 'TrustedURL' errors + // that require FE changes to work. + // "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 { - // 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" - // - // The list of CSP options: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src - cspSrcs := CSPDirectives{ - // All omitted fetch csp srcs default to this. - CSPDirectiveDefaultSrc: {"'self'"}, - CSPDirectiveConnectSrc: {"'self' ws: wss:"}, - CSPDirectiveChildSrc: {"'self'"}, - CSPDirectiveScriptSrc: {"'self'"}, - CSPDirectiveFontSrc: {"'self'"}, - CSPDirectiveStyleSrc: {"'self' 'unsafe-inline'"}, - // object-src is needed to support code-server - CSPDirectiveObjectSrc: {"'self'"}, - // blob: for loading the pwa manifest for code-server - CSPDirectiveManifestSrc: {"'self' blob:"}, - CSPDirectiveFrameSrc: {"'self'"}, - // data: for loading base64 encoded icons for generic applications. - // 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:"}, - CSPDirectiveFormAction: {"'self'"}, - CSPDirectiveMediaSrc: {"'self'"}, - // Report all violations back to the server to log - CSPDirectiveReportURI: {"/api/v2/csp/reports"}, - CSPFrameAncestors: {"'none'"}, - - // Only scripts can manipulate the dom. This prevents someone from - // naming themselves something like ''. - // "require-trusted-types-for" : []string{"'script'"}, - } - - var csp strings.Builder - for src, vals := range cspSrcs { - _, _ = fmt.Fprintf(&csp, "%s %s; ", src, strings.Join(vals, " ")) - } - // 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. From 4daf10867abd35392d0aaa05d5d76a42b8f3568f Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 29 Jun 2022 09:57:35 -0500 Subject: [PATCH 2/3] Big whoops copying over v1 csp settings --- site/site.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/site/site.go b/site/site.go index d4d303c0c1be1..91fdc171ccb45 100644 --- a/site/site.go +++ b/site/site.go @@ -256,7 +256,6 @@ const ( CSPFrameAncestors = "frame-ancestors" ) -// cspHeaders adds 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. @@ -278,17 +277,18 @@ func cspHeaders(next http.Handler) http.Handler { CSPDirectiveManifestSrc: {"'self' blob:"}, CSPDirectiveFrameSrc: {"'self'"}, // data: for loading base64 encoded icons for generic applications. - CSPDirectiveImgSrc: {"'self' https://cdn.coder.com data:"}, + // 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:"}, CSPDirectiveFormAction: {"'self'"}, CSPDirectiveMediaSrc: {"'self'"}, // Report all violations back to the server to log - CSPDirectiveReportURI: {"/api/private/csp/reports"}, + CSPDirectiveReportURI: {"/api/v2/csp/reports"}, CSPFrameAncestors: {"'none'"}, // Only scripts can manipulate the dom. This prevents someone from // naming themselves something like ''. - // TODO: @emyrk we need to make FE changes to enable this. We get 'TrustedHTML' and 'TrustedURL' errors - // that require FE changes to work. // "require-trusted-types-for" : []string{"'script'"}, } From 372fb62dd9a0d025413acdca723e5107e7e39564 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 29 Jun 2022 10:32:53 -0500 Subject: [PATCH 3/3] Drop uneeded whitelist from csp --- site/site.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/site.go b/site/site.go index 91fdc171ccb45..a8d1147381243 100644 --- a/site/site.go +++ b/site/site.go @@ -280,7 +280,7 @@ func cspHeaders(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