Monday, June 29, 2009

CSP: with or without meta?

We're working up a storm on Content Security Policy (CSP) here at Mozilla, and I've been spending a lot of time hacking out an implementation and talking with people about how CSP works. I keep coming back to sharp edges caused by allowing policies in <meta> tags. Not only does meta-tag support make implementation of CSP more difficult, but it actually also provides an additional attack surface.

What is CSP?
Quick summary of Content Security Policy: CSP lets web site authors specify a policy that locks down where the site may obtain resources as well as what types of resources may be requested. This policy is specified in an HTTP Request header or may also be specified in a meta tag. There's a great blog post by Brandon that talks about this stuff more in depth.

Enter the http-equivalent META tag.
Originally, the point of allowing policy definitions in meta tags was to gain greater flexibility and give an option to folks who can't modify HTTP headers due to web hosting restrictions. Later on, we started thinking that meta-tag-CSP would be a useful way to allow "tightening" or intersection of policies for specific segments of a web site.

The only use case that comes to my mind is a shared web hosting service. Imagine the controllers of a hosting service want to forbid embedding of Flash content from; at the same time their customers may want a more restrictive policy, but only one policy can be specified in HTTP. As a result the hosting company has three options:

  1. Let their customers override the policy (possibly removing the rule)

  2. Disallow the ability for their customers to tighten the CSP

  3. Provide some way to allow policy tightening without the possibility of loosening.

An ability to specify policies in meta tags gives way for situation 3: policy tightening. Unfortunately there are side-effects to allowing policy specification in both HTTP headers and meta tags.

Side Effects.
Implementing CSP becomes quite a bit more complex with meta tags. First, the user agent has to figure out to do when there are two conflicting policies, one in HTTP and one in meta. We solved this with an intersection algorithm that can only tighten an effective policy. Aside from conflicts, there's also the issue of parsing the meta tag out of the document appropriately before any resources subject to CSP are requested.

Allowing policy specification in a meta tag also opens up another use for successful content injection attacks: injection of an unauthorized policy. Additionally, such a policy could be used for a limited CSRF attack on the site itself through a policy-uri or report-uri directive. Of course an unauthorized "allow none" policy can effectively DoS a site, too.

Content Separation.
In the haze of thinking about meta-tag-CSP uses, I lost track of the reason CSP was HTTP header-based in the first place: to separate the content from the transmission channel and underlying policies that controls what it is for and what it can do. There's a clear advantage to this separation: it is harder to attack. Adversaries now must be able to break into the protocol level, not just the application/content. HTTP headers are way more easily hardened on the server-side than an HTML tag.

I want to eradicate meta-tag support for CSP, and all of the thorns that come with it -- policy intersection, document parsing complexity, HTML injection threats, etc -- because I don't think the relatively small gain from implementing it is worth the potential cost and risk. Is there a use (other than the hosting service use case above) that requires a meta tag CSP... and is worth the security risk and code complexity?


Wladimir Palant said...

I tend to agree with the removal of the meta tag. It will allow for quite a bit of simplification in both the spec and the implementation - and with these things the simpler solution is the better one, it will make it far more likely that other browsers adopt the same approach. The attack targets are typically dynamic pages, those can always change the HTTP headers (with things like SSI being a rare exception - and you have to try hard to make an SSI-based page vulnerable). So it is probably a safe bet that 99% of sites interested in using this feature won't have problems adding HTTP headers.

However, it might make sense to allow multiple policy-uri entries. A large website with lots of different applications might want to define a global (not too restrictive) policy. As it stands now, if a particular application needs a more restrictive policy you will need to make a copy of the global policy and modify it - and accept the resulting maintenance effort if the global policy ever changes. Not sure whether this is really an issue worth solving of course, just a thought.

Gerv said...

Why now allow multiple headers, and keep the intersection algorithm?

This way, the hosting company has to provide a special interface for editing the header rather than the customer just being able to type it into the page, but it still allows it to make non-negotiable restrictions. They just serve their restrictions in the first header, and make sure the customer-provided header comes afterwards.

This means you still need the policy intersection logic, so that part of complexity isn't removed, but it still allows, at least in some ways, the use case that you were worried about. A compromise, in other words.