mako-framework/framework

Add support for Content Security Policy nonces

Closed this issue · 6 comments

Description


A common technique to mitigate XSS attacks, is to use a Content Security Policy. The server sends a header detailing with a whitelist of scripts and resources that are allowed to be included in the page.

Scripts can be whitelisted by origin, secure hash, or with a nonce effectively signing the contents of the page. To implement such a nonce, you need to do the following:

  1. Generate a cryptographically secure nonce $nonce
  2. Send the CSP header containing said nonce `Content-Security-Policy: script-src nonce-$nonce;
  3. For every inline script, add the nonce to its opening tag <script nonce="$nonce">

It is currently doable to implement this, but it requires some awkward interactions between the Response object and the template being rendered. It would be nice if the Request object got some kind of native support for the content security policy mechanism to aid in implementing this. I have two ideas in mind shown below, but I'd love to hear comments.

Option 1: expose a cspNone() function.

The Response object gains a method cpsNonce(): string which will generate the proper nonce and store it for future use. This is really simple to implement but also not very helpful.

Option 2: add a content security policy object to the response.

We add a member (similar to the headers) to the response object to manage the CSP header and related work. This would be a sort of "out-of-the-box" solution where you declare your CSP in config files and if it contains a nonce option, you add the nonce to the header. This nonce can then be exposed in a similar way as above.

We use middleware to handle CSP and other security related headers at work. I'll see if I can extract the code and create a package or include it in the core.

The middleware could auto-assign a global $_csp_nonce view variable or replace a configurable token (e.g. @csp_nonce). It can also support both if that's desirable.

A global view variable would be great; I didn't think of that possibility.

https://gist.github.com/freost/0991a84d119be488367b148e680bdce0

Would something like this be sufficient? I has some sensible defaults and can be extended to add custom directives and headers.

Looks great. We use something similar but it can easily be replaced by this.

The middleware will be available in the 6.3 release. It also supports the new reporting api.