Creating Content Security Policy in SvelteKit
What is Content Security Policy (CSP)?
In a nutshell, Content Security Policy (CSP) is a list of trusted origins which only their resources can be loaded on your website once defined. This is a security feature for preventing attacks such as cross-site scripting (XSS) and data injection.
CSP can be defined through HTTP response headers Content-Security-Policy
and Content-Security-Policy-Report-Only
or meta tags with <meta http-equiv>
.
Setting up Content Security Policy in SvelteKit
SvelteKit provides CSP support out of the box. We just need to define the policy we want through kit.csp
, and SvelteKit will generate the corresponding hash or nonce for its inline style and scripts, and policies as <meta http-equiv>
tag in HTML.
Initially I defined my CSP based on the basic non-strict policy from OWASP. default-src
directive would be used as a fallback for most unspecified directives, except a few exceptions such as frame-ancestors
and form-action
directive. The values of a directive are the allowed origins, and self
represents the same origin as your website.
By defining our policies in kit.csp.reportOnly
instead of kit.csp.directives
, browsers would not block non-compliant resources but only log violations or make a POST request to the endpoint defined in report-uri
. With that information, you can discover resources your website depending on, and gradually tighten your security policies without breaking anything, by moving your security policies from kit.csp.reportOnly
to kit.csp.directives
.
const config = {
kit: {
csp: {
// NOTE without setting a truthy value for kit.csp.directives, SvelteKit would not do anything for CSP
directives: {},
reportOnly: {
"default-src": ["self"],
'frame-ancestors': ['self'],
'form-action': ['self'],
'report-uri': ['/'],
},
},
},
};
I have created a minimal SvelteKit project with these content security policies defined, so you can play around yourself.
Dealing with CSP violations in SvelteKit
The above configuration should work for most of the SvelteKit projects. From time to time you might encounter packages that violates your CSP. For example, on this website I use Grafana Faro for tracking performance my website. The SDK for this service, @grafana/faro-web-sdk
used a package contains eval()
in its code, I then notice the following log in my console.
Content-Security-Policy: (Report-Only policy) The page’s settings would block a JavaScript eval (script-src) from being executed because it violates the following directive: “script-src 'self' 'nonce-ewcvSmSO/2WM8RPgzlYn3A==' (Missing 'unsafe-eval')
An option to address this is to add unsafe-eval
to script-src
directive as suggested, but this is a global setting for my website, not only for this package. We won’t get much benefit from CSP if we had used unsafe-eval
or unsafe-inline
.
The other option is we move the policies to kit.csp.reportOnly
for reporting violations only, and we monitor those violations reports to verify our website is secure or not. You could host a server that receives and converts the POST payload sent by Reporting API from browsers to metrics understandable by tools such as Prometheus or OpenTelemetry, and then set up alerting rules to monitor content security policy’s violations. This approach requires more work in the back end, but it saves you from refactoring code or switch packages in your SvelteKit project.
From my research, I found go-csp-collector for collecting the reports, and it seems to be a good starting point for building your monitoring system.