This is a personal blog. My other stuff: book | home page | Twitter | G+ | CNC robotics | electronics

August 26, 2011

The subtle / deadly problem with CSP

Content Security Policy is a promising new security mechanism deployed in Firefox, and on its way to WebKit. It aims to be many things - but its most important aspect is the ability to restrict the permissible sources of JavaScript code in the policed HTML document. In this capacity, CSP is hoped to greatly mitigate the impact of cross-site scripting flaws: the attacker will need to find not only a markup injection vulnerability, but also gain the ability to host a snippet of malicious JavaScript in one of of the whitelisted locations. Intuitively, that second part is a much more difficult task.

Content Security Policy is sometimes criticized on the grounds of its complexity, potential performance impact, or its somewhat ill-specified scope - but I suspect that its most significant weakness lies elsewhere. The key issue is that the granularity of CSP is limited to SOP origins: that is, you can permit scripts from http://www1.mysite.com:1234/, or perhaps from a wildcard such as *.mysite.com - but you can't be any more precise. I am fairly certain that in a majority of real-world cases, this will undo many of the apparent benefits of the scheme.

To understand the problem, it is important to note that in modern times, almost every single domain (be it mozilla.org or microsoft.com) hosts dozens of largely separate web applications consisting of hundreds of unrelated scripts - quite often including normally inactive components used for testing and debugging needs. In this setting, CSP will prevent the attacker from directly injecting his own code on the vulnerable page - but will still allow him to put the targeted web application in a dangerously inconsistent state, simply by loading select existing scripts in the incorrect context or in an unusual sequence. The history of vulnerabilities in non-web software strongly implies that program state corruption flaws will be exploitable more often than we may be inclined to suspect.

If that possibility is unconvincing, consider another risk: the attacker loading a subresource that is not a genuine script, but could be plausibly mistaken for one. Examples of this include an user-supplied text file, an image with a particular plain-text string inside, or even a seemingly benign XHTML document (thanks to E4X). The authors of CSP eventually noticed this unexpected weakness, and decided to plug the hole by requiring a whitelisted Content-Type for any CSP-controlled scripts - but even this approach may be insufficient. That's because of the exceedingly common practice of offering publicly-reachable JSONP interfaces for which the caller has the ability to specify the name of the callback function, e.g.:

GET /store_locator_api.cgi?zip=90210&callback=myResultParser HTTP/1.0
...

HTTP/1.0 200 OK
Content-Type: application/x-javascript
...
myResultParser({ "store_name": "Spacely Space Sprockets",
                 "street": ... });
Having such an API anywhere within a CSP-permitted origin is a sudden risk, and may be trivially leveraged by the attacker to call arbitrary functions in the code (perhaps with attacker-dependent parameters, too). Worse yet, if the callback string is not constrained to alphanumerics – after all, until now, there was no compelling reason to do so – specifying callback=alert(1);// will simply bypass CSP right away.

The bottom line is that CSP will require web masters not only to create a sensible policy, but also thoroughly comb every inch of the whitelisted domains for a number of highly counterintuitive but potentially deadly irregularities like this. And that's the tragedy of origin scoping: if people were good at reviewing their sites for subtle issues, we would not be needing XSS defenses to begin with.

6 comments:

  1. Given the choice between blaming CSP or JSONP, personally I'd go after the latter. Reference http://json-p.org for some of JSONP's dirty laundry. I have to imagine the specific issue you pointed out is a challenge that can be overcome in CSP. Dinging the CSP folks too hard over a bypass scenario could have the unintended side effect of making it harder to justify investment in this space, and nobody wants that. (Seriously, your voice carries a lot of weight. ;-))

    ReplyDelete
  2. I'm not trying to ding them, I am just not sure that origin scoping is the best way to go about it - perhaps as little as offering choice between origin scoping and individual URLs is a good approach.

    Of course, the problem with listing individual URLs in CSP is that it almost makes the scheme pointless: if you're doing that, why not just decouple the list of loaded scripts from the markup completely.

    Ultimately, I sort of suspect that the best approach to fixing XSS may be simply to give webservers the ability to transmit binary DOM tree to the client, rather than relying on HTML serialization in between.

    This would remove many of the common templating bugs, it offers significant performance benefits, is not as restrictive as CSP (the inability for people to use inline on* handlers, and the need for a separate HTTP request to retrieve scripts, is a big deal), and isn't exactly outlandish: after all, WebKit has SPDY for binary HTTP, and Firefox may be getting it, too.

    There is an argument to be made that the two mechanisms are essentially separate, so there is no harm doing CSP now, and thinking about DOM-over-the-wire later. That said, there is an element of discouragement: notice, for example, that CSP originally tried to prevent XSRF, but they have given up of that goal, delegating it to the "Origin" proposal, which seemed to be going strong back then. Unfortunately, that effort largely died off, so CSP is left as a not-quite-all-encompassing-security-framework - which I think prompted the authors to distract themselves with non-security goals, flirting with what could be described just as "Content Policy".

    That, in turn, had a negative effect on the security of the scheme: by focusing rulesets on specific tags, rather than on their security implications, it's very easy to create bad policies (disallowing scripts but allowing script-equivalent embeds, permitting data: URLs for scripts, introducing mixed content bugs).

    ReplyDelete
  3. It seems like creating a subdomain for deploying your web app's trusted JavaScript files would solve this problem fairly easily?

    ReplyDelete
  4. Partly. Does not prevent the concerns with out-of-order / out-of-context scripts, and increases the DNS cost.

    On the flip side: even without CSP, isolating every functional block of a large site in a separate subdomain, coupled with httponly cookies, would also tremendously mitigate the impact of XSS. Almost nobody routinely does that, though.

    ReplyDelete
  5. "Ultimately, I sort of suspect that the best approach to fixing XSS may be simply to give webservers the ability to transmit binary DOM tree to the client, rather than relying on HTML serialization in between."

    It seems to me that the security benefit is all contained within the first part of this scheme, which is the construction of the safe DOM tree at the server. If you can build a safe DOM tree at the server then why not just serialize that to safe HTML. If that isn't possible it would indicate a bug w/serialization more than anything else. Serving HTML vs. a binary blob would preserve compatibility with existing browsers and avoid enabling additional client-side attack surface.

    ReplyDelete
  6. Yeah, sure.

    The main reason it's probably not going to happen on its own is that it offers no obvious developer benefit (everybody thinks they are perfectly capable of escaping everything just fine on their own). Getting rid of the serialization / parsing steps, and optimizing the size of over-the-wire data, would probably offer a pretty significant performance benefit - so that's a way to get your foot in the door.

    ReplyDelete