Thursday, May 23, 2013

Tip of the tree Blink now has CSP 1.1 script nonce support! And what the hell does that mean?

As a new Googler, things can be slow going. There's a lot to pick up here. New tools to learn, new systems to navigate, new people to interact with, and, of course, new code to explore and understand. So, naturally, I was quite excited when I made what was really my first substantial contribution to Blink (the rendering engine underneath Chromium that recently replaced WebKit), and I made the following Tweet about it:

This left a lot people confused. Clearly I was excited about it, but why? So I thought I'd clarify here.

For those of you who don't know, Content Security Policy (CSP) is a browser mechanism for helping to eliminate cross-site scripting (XSS) vulnerabilities in web sites. Now, there are a variety of caveats to that statement, namely that CSP requires developers to code in a more limited style, requires the server to send specific headers, and ultimately still allows developers to shoot themselves in the foot. But, for now, let's assume it makes the world a better place if used1.

One of the limitations of CSP that needs to be enforced for it to effectively block XSS attacks is the requirement that the page contains absolutely no inline scripts. This means absolutely no script tags with code, e.g.<script>alert('foobar');</script>. It also means no sneaky inline handlers or javascript: protocol URLs, e.g. <button onclick="alert('foobar');">click me!</button> or <img src="javascript:alert('foobar');"></img>. To be clear, this is part of the policy that CSP enforces. That is, you can try to insert those on your page, but CSP will block those inline scripts from executing, which is a lot of where the anti-XSS magic from CSP lies.

Why this requirement? Well, one of the insights of the creators of CSP is that the way XSS often happens is when a web server puts untrusted content on a page, and sanitizes the content incorrectly (or does not sanitize it at all). Then, even though the content is not expected to contain a script, the bad guy inserts a script which is served up to unsuspecting users. So CSP prevents these scripts for executing, and therefore if a bad guy tries to insert them, they simply will not execute.

By the same token, CSP allows external scripts to be loaded (i.e. <script src="some/path/here.html"></script>), but only from an explicitly chosen set of servers. Thus, a bad guy could insert a script tag that loads a script from a URL, but the he would be limited to only a small set of servers, ostensibly controlled by the developer. So getting an XSS attack this way will be much, much more difficult.

But the ban on inline scripts is a big limitation that, for many developers, will be a huge change. Although it's not impossible (hey, Twitter did it), the process might be very painful. For example, take a third party widget that you want to include on your site that relies on inserting an inline script into your page. It's possible that it might work off the bat just by putting it in an external script, but there's a good chance it will require a lot of hacks, if you can get it working at all. At the same time, perhaps this widget requires on completely static, known JavaScript, so we're not worried about a bad guy putting anything into the script itself.

Enter the idea of a script whitelist. The creators of the latest version of CSP, CSP 1.1, realized that we don't want to ban all inline scripts; we want to ban all unknown inline scripts. But how do we specify known, whitelisted scripts? This is where nonces come into play.

A nonce is a use-once, randomly generated number, that is unforgeable. Practically speaking, that just means it's a big, random number. What CSP 1.1 introduces is the ability for a server to list a set of newly generated nonces every time a page is loaded that can be used to whitelist scripts. Then, when the page is loaded, the developer may include inline scripts, but only if she specifies the script with a valid nonce. Say the server specifies with the page that 9253884 is a valid nonce. CSP 1.1 allows a developer to write the following:

<script nonce="9253884">
alert('foobar');
</script>

The clever part here is that the inline script specifies a secret that only the good guy could know. That is, a bad guy could still try to insert <script> tags on the page, but because the nonce is not guessable, his script will be rejected by the browser. And there you have it: the benefits of CSP while allowing inline scripts. There are actually a couple other uses of CSP 1.1 nonces, but I'll leave them out here because this is definitely the main use.

In short, while Chrome has had CSP for a while now, we're trying to get up to the CSP 1.1 spec so that we can provide developers and users with even more awesome security and usability benefits. The implementation of this nonce support in the latest development version of Blink is just a small part in a much bigger picture, but I hope it will go a long way in helping developers and spreading the adoption of CSP.

Clarification: I skipped over this, but was quickly called out on Twitter. Script nonces can additionally be used to include external scripts that are not from whitelisted sources. That is, CSP requires that all external scripts are only loaded from a set of whitelisted servers. However, if you specify a valid nonce, you can bypass this requirement.

Update on 2013-07-23: Because the nonce spec is not quite settled, we've moved it behind a run time experimental flag for Content Security Policy features. You can still test it out, but you just need to be sure to run Chrome with that flag. You can enabled going to chrome://flags and selecting "enable" beneath "Enable experimental Web Platform features."1
In the past, I've actually done some research on the topic of CSP limitations, and there are a bunch. On the whole, though, I definitely believe it to be a boon for the Web. For the ultimate skeptic turned believer, see Adam Barth's blog post on CSP.
↩