Programming is difficult — and difficult things generally don’t have a perfect solution. As an example, cross-site scripting (XSS) is still very much unsolved. It’s very easy to think you’re doing the right thing at the right time, but there are two opportunities to fail here: the fix might not be correct, and it might not be applied correctly. Escaping content (while still the most effective way to mitigate XSS) has a lot of “gotchas” (such as contextual differences and browser quirks) that show up time and time again. Content Security Policy (CSP) is an additional layer of security on top existing controls.
Twitter has recently expanded our use of response headers in order to leverage the protection they provide via the browser. Headers like X-Frame-Options (for clickjacking protection) and Strict Transport Security (for enforcing SSL) are somewhat common these days, but we’re here to discuss and recommend Content Security Policy.
What is Content Security Policy?
But wait! There’s more. CSP can be used to ensure that you don’t cause any mixed-content issues that can lead to session token theft or other tampering with user experience (see “replace all pictures with lolcats” for resources loaded over HTTP). You can also ensure that content is only loaded from hosts that you trust. In a sense, the CSP header documents the integrity of your web page inside a response header.
I know what you’re thinking: This sounds disruptive, potentially flawed, and could really break my site without even realizing it! CSP violations will cause the browser to POST json content to the specified report-uri. There’s also a “report-only” mode in which violations are reported without actually changing any behavior. You can analyze the values of these reports by inspecting the browser console as well as aggregating the reports that are sent to the “report-uri” value. The reports contain useful information such as the type of violation that occurred, the action that was blocked, the part of the policy that was violated, and other environmental information that can help describe what happened. You can use these reports to tune the policies over time and look for anomalous behavior (which might indicate a successful exploit).
How CSP was implemented at Twitter
Application of the CSP header was previously done inconsistently. Differences in browser implementations compounded the issue. That problem was solved by providing easy-to-use solutions that abstract browser-specific header implementations. This involved augmenting frameworks or providing libraries to manage these headers. Our ruby implementation (secure_headers) is open sourced and this has Ruby on Rails support to make the application of CSP and other headers minimally painful. We have built similar, but not identical, functionality in the framework which all Scala web projects are built upon.
We were really surprised with what kinds of reports we were seeing. By examining the CSP reports we found foreign domains pointing at our hosts, potential indicators of infected browsers/computers, non-security-related bugs in site functionality, as well as how popular certain browser extensions are among our visitors. Most plugins aren’t CSP-friendly so this can generate a lot of noise. Don’t worry, we don’t aggregate the data with identifying information, all reports are treated as anonymous.
What about CSP and Twitter today and the future?
In some situations, like high latency connections, inline scripts/styles perform better due to the lower number of connections required to satisfy a window load. In these cases, we remove inline script/styles for users with fast connections, and leave the scripts/styles in for the rest. There are a few browser quirks and workarounds that require inline script but we’re confident these things will be phased out over time. As these blockers fade away, the header will continue to proliferate until we have 100% coverage. We track the application of CSP, as well as other “security headers” on a dashboard - full coverage means all cells are showing green.
Another interesting proposal is the concept of a DOM API for interacting with CSP. The ability to interact with CSP will provide the ability to toggle whether client-side capabilities/workarounds should be enabled/disabled based on how the features would interact with CSP protection. Conversely, 1.1 also specifies the ability to disable the eval function via the API. In the case where a library will only do an eval on initialization, we can disable eval after initialization to keep the policy very strict.
As noted in the Github Content Security Policy post, plugin and bookmarklet implementation in browsers differs with the spec text in that violations from these sources break functionality and generate reports. This is definitely a pain point when reviewing reports. Unfortunately, this isn’t a trivial fix for the browser implementers. The usefulness of CSP reports will skyrocket once this is implemented.
Applying CSP sitewide is an effort that involved every web development team in the company. We could not have done this without the support of the developers on the ground, who sometimes before getting a full blessing from their peers, would investigate and/or submit a patch or shepherd my attempt to apply the policy. We have been practicing the “if you build it, they will come” strategy, and so far it seems to work very well for response headers, especially CSP.
CSP 1.0 Spec: http://www.w3.org/TR/CSP/
CSP 1.1 Spec (work in progress): https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html
Web Application Security Working Group: http://www.w3.org/2011/webappsec/
secure_headers library: https://github.com/twitter/secureheaders
Introduction to CSP - Mike West: http://www.html5rocks.com/en/tutorials/security/content-security-policy/
Security Headers on the Top 1,000,000 Websites - Veracode: http://www.veracode.com/blog/2013/03/security-headers-on-the-top-1000000-websites-march-2013-report/
Did someone say … cookies?