CSP to the Rescue: Leveraging the Browser for Security

Thursday, 6 June 2013

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?

Content Security Policy (CSP) is a whitelisting mechanism that allows you to declare what behavior is allowed on a given page. This includes where assets are loaded from, where forms can send data, and most importantly, what JavaScript is allowed to execute on a page. This is not the first time we’ve blogged about CSP or have dealt with CSP related vulnerabilities:

 

CSP empowers you to disallow inline JavaScript including onclick and other DOM events, links with “JavaScript:” values, and <script> blocks in the HTML content of a page. This feature effectively eliminates all stored and reflected XSS. Here’s an example of using CSP to disable the content inside a script tag:

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).

CSP Reports

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.

That was just the technical challenge. We decided to fully invest in the technology and doing so would require a moratorium on inline JavaScript and eventually inline styles. We did receive some pushback, but the support was overwhelming and coming from all directions and all levels. This allowed us to discuss CSP as a company-wide development guideline which in turn led to the almost sitewide application of this header.

A side effect of this process was that we realized contextual encoding of dynamic content is an anti-pattern. Contextual encoding promotes using dynamic content in any situation so long as it is properly encoded. This complexity is unnecessary and error-prone. By removing all inline JavaScript and inline event handlers, we completely eliminated the need to encode for the JavaScript context. Dynamic values can be html entity encoded and placed in element attributes or in the body of tags which is read by the external JavaScript. The techniques used to apply this strategy can be found here. This can be adopted as a company wide standard because all frameworks have html entity encoding built in.

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?

As you probably can tell, you don’t see CSP on Twitter.com today. As one of the oldest projects, there is a wealth of inline JavaScript which is delaying our rollout for all of our public properties. We have also encountered a few cases which require inline script for functionality or performance issues that are prolonging the process which necessitates exceptions for endpoints that are not ready for CSP. However, CSP is applied to all other Twitter properties including Twitter Mobile, Twitter Ads Center, Translation Center, Twitter for Business, Tweetdeck and more. In fact, this post is hosted on our new blogging platform, which applies a strict policy too.

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.

CSP 1.1 is proposing important changes that will make changes that will help make CSP a viable option for a wider audience. In addition to making the existing specification easier to use, new features are also being worked on. Currently, there is a discussion related to how to can use CSP Javascript execution protection without being required to remove all inline script blocks. While removing the inline content is still your best option due to wider adoption and reduced complexity, CSP 1.1 allows you to whitelist individual script blocks. This is accomplished by providing a fingerprint and whitelisting the values in the header.

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.

Further reading

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/
Removing inline JavaScript (for CSP): http://nmatatal.blogspot.com/2013/01/removing-inline-javascript-for-csp.html