Skip to content

Commit 889e2e6

Browse files
authored
security: Tighten csp connect-src to prevent external websockets (#2705)
1 parent ea7f9e2 commit 889e2e6

File tree

1 file changed

+62
-44
lines changed

1 file changed

+62
-44
lines changed

site/site.go

+62-44
Original file line numberDiff line numberDiff line change
@@ -256,48 +256,70 @@ const (
256256
CSPFrameAncestors = "frame-ancestors"
257257
)
258258

259+
func cspHeaders(next http.Handler) http.Handler {
260+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
261+
// Content-Security-Policy disables loading certain content types and can prevent XSS injections.
262+
// This site helps eval your policy for syntax and other common issues: https://csp-evaluator.withgoogle.com/
263+
// If we ever want to render something like a PDF, we need to adjust "object-src"
264+
//
265+
// The list of CSP options: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src
266+
cspSrcs := CSPDirectives{
267+
// All omitted fetch csp srcs default to this.
268+
CSPDirectiveDefaultSrc: {"'self'"},
269+
CSPDirectiveConnectSrc: {"'self'"},
270+
CSPDirectiveChildSrc: {"'self'"},
271+
CSPDirectiveScriptSrc: {"'self'"},
272+
CSPDirectiveFontSrc: {"'self'"},
273+
CSPDirectiveStyleSrc: {"'self' 'unsafe-inline'"},
274+
// object-src is needed to support code-server
275+
CSPDirectiveObjectSrc: {"'self'"},
276+
// blob: for loading the pwa manifest for code-server
277+
CSPDirectiveManifestSrc: {"'self' blob:"},
278+
CSPDirectiveFrameSrc: {"'self'"},
279+
// data: for loading base64 encoded icons for generic applications.
280+
// https: allows loading images from external sources. This is not ideal
281+
// but is required for the templates page that renders readmes.
282+
// We should find a better solution in the future.
283+
CSPDirectiveImgSrc: {"'self' data:"},
284+
CSPDirectiveFormAction: {"'self'"},
285+
CSPDirectiveMediaSrc: {"'self'"},
286+
// Report all violations back to the server to log
287+
CSPDirectiveReportURI: {"/api/v2/csp/reports"},
288+
CSPFrameAncestors: {"'none'"},
289+
290+
// Only scripts can manipulate the dom. This prevents someone from
291+
// naming themselves something like '<svg onload="alert(/cross-site-scripting/)" />'.
292+
// "require-trusted-types-for" : []string{"'script'"},
293+
}
294+
295+
// This extra connect-src addition is required to support old webkit
296+
// based browsers (Safari).
297+
// See issue: https://github.com/w3c/webappsec-csp/issues/7
298+
// Once webkit browsers support 'self' on connect-src, we can remove this.
299+
// When we remove this, the csp header can be static, as opposed to being
300+
// dynamically generated for each request.
301+
host := r.Host
302+
// It is important r.Host is not an empty string.
303+
if host != "" {
304+
// We can add both ws:// and wss:// as browsers do not let https
305+
// pages to connect to non-tls websocket connections. So this
306+
// supports both http & https webpages.
307+
cspSrcs.Append(CSPDirectiveConnectSrc, fmt.Sprintf("wss://%[1]s ws://%[1]s", host))
308+
}
309+
310+
var csp strings.Builder
311+
for src, vals := range cspSrcs {
312+
_, _ = fmt.Fprintf(&csp, "%s %s; ", src, strings.Join(vals, " "))
313+
}
314+
315+
w.Header().Set("Content-Security-Policy", csp.String())
316+
next.ServeHTTP(w, r)
317+
})
318+
}
319+
259320
// secureHeaders is only needed for statically served files. We do not need this for api endpoints.
260321
// It adds various headers to enforce browser security features.
261322
func secureHeaders(next http.Handler) http.Handler {
262-
// Content-Security-Policy disables loading certain content types and can prevent XSS injections.
263-
// This site helps eval your policy for syntax and other common issues: https://csp-evaluator.withgoogle.com/
264-
// If we ever want to render something like a PDF, we need to adjust "object-src"
265-
//
266-
// The list of CSP options: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src
267-
cspSrcs := CSPDirectives{
268-
// All omitted fetch csp srcs default to this.
269-
CSPDirectiveDefaultSrc: {"'self'"},
270-
CSPDirectiveConnectSrc: {"'self' ws: wss:"},
271-
CSPDirectiveChildSrc: {"'self'"},
272-
CSPDirectiveScriptSrc: {"'self'"},
273-
CSPDirectiveFontSrc: {"'self'"},
274-
CSPDirectiveStyleSrc: {"'self' 'unsafe-inline'"},
275-
// object-src is needed to support code-server
276-
CSPDirectiveObjectSrc: {"'self'"},
277-
// blob: for loading the pwa manifest for code-server
278-
CSPDirectiveManifestSrc: {"'self' blob:"},
279-
CSPDirectiveFrameSrc: {"'self'"},
280-
// data: for loading base64 encoded icons for generic applications.
281-
// https: allows loading images from external sources. This is not ideal
282-
// but is required for the templates page that renders readmes.
283-
// We should find a better solution in the future.
284-
CSPDirectiveImgSrc: {"'self' https: https://cdn.coder.com data:"},
285-
CSPDirectiveFormAction: {"'self'"},
286-
CSPDirectiveMediaSrc: {"'self'"},
287-
// Report all violations back to the server to log
288-
CSPDirectiveReportURI: {"/api/v2/csp/reports"},
289-
CSPFrameAncestors: {"'none'"},
290-
291-
// Only scripts can manipulate the dom. This prevents someone from
292-
// naming themselves something like '<svg onload="alert(/cross-site-scripting/)" />'.
293-
// "require-trusted-types-for" : []string{"'script'"},
294-
}
295-
296-
var csp strings.Builder
297-
for src, vals := range cspSrcs {
298-
_, _ = fmt.Fprintf(&csp, "%s %s; ", src, strings.Join(vals, " "))
299-
}
300-
301323
// Permissions-Policy can be used to disabled various browser features that we do not use.
302324
// This can prevent an embedded iframe from accessing these features.
303325
// 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 {
322344
}, ", ")
323345

324346
return secure.New(secure.Options{
325-
// Set to ContentSecurityPolicyReportOnly for testing, as all errors are printed to the console log
326-
// but are not enforced.
327-
ContentSecurityPolicy: csp.String(),
328-
329347
PermissionsPolicy: permissions,
330348

331349
// Prevent the browser from sending Referer header with requests
332350
ReferrerPolicy: "no-referrer",
333-
}).Handler(next)
351+
}).Handler(cspHeaders(next))
334352
}
335353

336354
// htmlFiles recursively walks the file system passed finding all *.html files.

0 commit comments

Comments
 (0)