Content Security Policy (CSP) is an additional security feature that tells the browser what external sources a page can load and how it can be displayed. It protects against the injection of malicious code and attacks such as XSS. It is sent as a header composed of a series of directives. However, implementing it is not trivial.
Typically, we want to use JavaScript libraries located outside our server, such as Google Analytics, advertising systems, captchas, etc. Unfortunately, the first version of CSP fails here. It requires a precise analysis of the content loaded and the setting of the correct rules. This means creating a whitelist, a list of all the domains, which is not easy since some scripts dynamically pull other scripts from different domains or are redirected to other domains, etc. Even if you take the effort and manually create the list, you never know what might change in the future, so you must constantly monitor if the list is still up-to-date and correct it. Analysis by Google showed that even this meticulous tuning ultimately results in allowing such broad access that the whole purpose of CSP falls apart, just sending much larger headers with each request.
CSP level 2 approaches the problem differently using a nonce, but only the third version of the solution completed the process. Unfortunately, as of 2019, it does not have sufficient browser support.
Regarding how to assemble the script-src
and
style-src
directives to work correctly even in older browsers and
to minimize the effort, I have written a detailed
article in the Nette partner section. Essentially, the resulting form might
look like this:
script-src 'nonce-XXXXX' 'strict-dynamic' * 'unsafe-inline' style-src 'nonce-XXXXX' * 'unsafe-inline'
Example of Use in PHP
We generate a nonce and send the header:
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic' * 'unsafe-inline'");
And we insert the nonce into the HTML code:
<script nonce="<?=$nonce?>" src="..."></script>
Example of Use in Nette
Since Nette has built-in support for CSP and nonce since version 2.4, simply specify in the configuration file:
http:
csp:
script-src: [nonce, strict-dynamic, *, unsafe-inline]
style-src: [nonce, *, unsafe-inline]
And then use in templates:
<script n:nonce src="..."></script>
<style n:nonce>...</style>
Monitoring
Before you set new rules for CSP, try them out first using the
Content-Security-Policy-Report-Only
header. This header works in
all browsers that support CSP. If a rule is violated, the browser does not block
the script but instead sends a notification to the URL specified in the
report-uri
directive. To receive and analyze these notifications,
you might use a service like Report
URI.
http:
cspReportOnly:
script-src: [nonce, strict-dynamic, *, unsafe-inline]
report-uri: https://xxx.report-uri.com/r/d/csp/reportOnly
You can use both headers simultaneously, with
Content-Security-Policy
having verified and active rules and
Content-Security-Policy-Report-Only
to test their modifications. Of
course, you can also monitor failures in the strict rules.