Hey browser, these guys are with me

27 Apr 2017

This covers something I learnt when, having joined a project late I was asked to pick up some outstanding actions from their recent PEN test. Specifically the test had highlighted that the service was missing a Content Security Policy (CSP).

A what now?

A CSP is delivered via a HTTP response header, and defines approved sources of content that the browser may load. It’s seen as an effective countermeasure to Cross Site Scripting (XSS) attacks, is widely supported and usually easy to deploy.

When your browser loads a page it also loads a bunch of other assets along with it; images, fonts, styles, and JavaScript. It does this because we’ve told it to do so via things like our script tags.

The browser however doesn’t differentiate. So if a hacker, via some content they have contributed (a comment is the classic example), manages to inject a properly formed script tag into the page the browser will load it along with everything else.

So a CSP is designed to prevent this by essentially adopting the idea of a white list. The policy sets out what are the valid sources for the various types of assets your page may load, so the browser can differentiate.

Getting it in there

The project already had code for adding custom headers, specifically to manage caching. This was done within the application controller (oh, and its a Rails project by the way) using a before_action filter to call a method that did something like this.

response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate, private"

So the initial implementation simply added something along the lines off

response
  .headers["Content-Security-Policy"] = "default-src 'self'; "\
                                        "script-src 'self' 'unsafe-inline'; "\
                                        "font-src 'self' data:; "\
                                        "report-uri https://mycustomname.report-uri.io/r/default/csp/enforce"

Specifically we are saying

All done?

Not quite! For reasons we spotted that this didn’t cover assets we were bringing in to support Google Analytics. Also one of those assets was an inline JS script, which our current use of unsafe-inline covered. But this was intended to only be temporary until an issue in a dependency was resolved and we could switch it off. So ideally we wanted to also be able to support nonces. Basically these are one time values generated on the server and added to the script tag, but also into the CSP. The browser can then cross check them, see they match and therefore permit the specified inline-script to run.

At this point the temptation is start rolling your own solution but a quick search found one already existed; enter the Secure Headers gem.

Not only did this come with support for nonces, but it also highlighted a bunch of other headers we really should be thinking of using.

To implement add a file to config/initializers (we called it secure_headers.rb). Then configure it using something like this

require `secure_headers`

SecureHeaders::Configuration.default do |config|
  config.csp = {
    default_src: %w('self'),
    font_src: %w('self' data:),
    img_src: %w('self' www.google-analytics.com),
    object_src: %w('self'),
    script_src: %w('self' 'unsafe-inline' www.googletagmanager.com www.google-analytics.com),
    style_src: %w('self'),
    report_uri: %w(https://mycustomname.report-uri.io/r/default/csp/enforce)
  }
end

So now we’re saying

(By the way, big thank you to Foundeo Inc for putting up an faq post on CSP that covered Google Analytics).

Further reading and references

There are loads of good doc’s on the specifics of how to define a security policy.

To really road test your site I’d also highly recommend Scott’s online tool securityheaders.io. If you’re like me you’ll run this tool and then immediately want to aim for an A+!

We also ended up using his CSP violation reporting tool report-uri.io as a simple to use, and most importantly free solution for capturing any violations.

Creative Commons Licence
Hey browser, these guys are with me by Alan Cruikshanks is licensed under a Creative Commons Attribution 4.0 International License.
Based on the work at http://cruikshanks.co.uk/blog/hey-browser-these-guys-are-with-me/.