Re: Using CSP

We are actively using CSP for several months in different environments, 
and we finally manage (I hope) to solve all issues. I'll share my 
experience here, as a lot of things come from experience to discover 
bugs, undocumented (or not completely documented) features, etc.

I'd like to thank Brandon who helped us a lot to understand all of these 
things. I hope all of these clarifications will end up in the spec, 
maybe with some examples (use cases).

First of all, we are maybe in an easy situation, as our main goal is to 
manage the CSP in a WAF, thus in front of applications. Our way of 
working may not work with management at the application level.

1. Whatever you want, you may use only one header.
Whether you want to restrict or to relax a rule in a sub-location, don't 
bother to try to add a header (or even a directive inside the header), 
it does not work. You have to specify, for each request, the exact and 
complete directive (ex: "img-src 'self' http://images.partner.com"). 
Note that "img-src 'self'; img-src http://images.partner.com" does not work.
I said we are in an easy situation with the CSP management in the WAF, 
because we dynamically build our header by adding all sources to the 
needed directive in the single header.

2. In a general context, the 'self' keyword is buggy.
I say "in general", because it obviously work in some cases (maybe even 
in most of the cases).
The problem is that if you use, for instance, "frame-src 'self'" to 
ensure that your pages cannot be framed in another site, 'self' is 
interpreted as "the same site and protocol as the originating page". 
This sounds good. It even protects you against framing your HTTPS page 
in a HTTP frame of your own site, as this would introduce an attack 
possibility. However, if you have a HTTPS frame inside your HTTP page, 
it's also blocked - that's definitely not the behaviour you want; this 
should be fixed in a next version. For the moment, if you want to allow 
HTTPS sources from HTTP pages, the only solution is to hard-code both 
protocols and sites in the directive (ex: "script-src 
http://www.mysite.com https://www.mysite.com"). There is no possibility 
like "https://'self'" to not specify the site name.
Again, we are lucky to manage that in the WAF, as we now the site name 
(from the Host: header in the request) and we dynamically build the 
sources with that name, allowing either both protocols or HTTPS only 
depending on the location need).

3. The origin is always considered to be the page "executing" a 
resource, not the page containing it. This is important for scripts and 
CSS included in separate files.
Ex: you have a page index.html that includes a file scripts.js; the 
script in script.js contains eval(). You need to specify that you allow 
the use of eval() in your script: "options eval-script" (or "script-src 
unsafe-eval" in the new syntax).
Sounds easy, but on which request do you have to send that header: 
index.html or scripts.js?
Actually, the answer is index.html, because the script finally gets 
executed on that page.
Although my main point is to clarify this, we may also discuss the 
logic: isn't this too relax? Because I use eval() in one of my script 
files, I have to open the page for any use of eval() anywhere (even in 
in-line scripts if I allow it).
Obviously, specifying it at scripts.js level would introduce a security 
risk if the page is located on another site (that would delegate the 
responsibility).
Any better option?

4. Backward compatibility
Although we did not test the new spec yet, we already implemented a 
config that encompasses both. It seems that the only drawback is that 
you have to specify the same thing twice. Ex: to allow eval(): "options 
eval-script; script-src unsafe-eval https://api.google.com". This 
shouldn't hurt.
This definitely works with FF 4/5 which accepts "unsafe-eval" without 
complaints (maybe it interprets this as a host name?). I hope future 
versions will accept (at least ignore) the deprecated syntax (mainly 
"options"), then we will be safe.

5. Now, in the practice, how do we manage all of this?

a.We begin with the most strict settings:
    "allow https://%{HOST}e" (for HTTPS locations)
   or
    "allow http://%{HOST}e https://%{HOST}e" (for HTTP locations)

b. Per location, we add exceptions to relax things
    "options eval-script; script-src unsafe-eval"
   Note that this is actually merged with existing directive
   Ex: "script-src api.google.com" -> "script-src unsafe-eval 
api.google.com"

c. Per location, we add exceptions to close things
   - disable external scripts: "script-src ..." -> ""
   - disable eval(): "unsafe-eval" -> ""
   - accepts only HTTPS sources: "http://... https://..." -> "https://..."
   etc.

Although this approach will not be possible for everybody (so 
combination of headers should be possible to allow 
adding/removing/resetting sources), I hope this will help some people.

About the combinations:
To allow combination, why not accepting
- "script-src +api.google.com" to allow this source on top of the other ones
- "script-src -*.google.com" to remove this source while keeping the 
previous ones
- "script-src *" to remove all sources
The only difficulty I see is that the directives must be added the 
logical way (from the global context up to the most precise one). This 
should not be a problem by appending directives to one header. It may be 
problematic if we want to use several headers (will they stay in the 
same order?).

Regards,

Nick


On 20/7/2011 8:14, Mark Nottingham wrote:
>
> On 20/07/2011, at 3:35 PM, Adam Barth wrote:
>>>
>>> - An example of the 'options ...' syntax in the spec would help; I had to look at the BNF before I realised I couldn't just put a bare 'inline-script' directive in the header.
>>
>> The "options" syntax got removed at some point.  I think Brandon is
>> updating the Firefox implementation to the new syntax.
>
> Hmm. If the syntax is still evolving in non-backwards-compatible ways, it might be better to use a nonsense or generated header name, and revise it on each bump, so that sites that experiment with CSP don't have problems with supporting multiple incompatible deployed protocols.
>
>
>>> One follow-up to that; I haven't enabled reporting yet, but if I did, I'd be flooded by reports of this. Having some mechanism to squelch a particular warning would be nice.
>>
>> Any suggestions on how to accomplish that?  In some sense, it's not
>> that much extra traffic.  You'd get just as much extra traffic by
>> including one additional image on your page.  You can always filter at
>> the server.
>
> True; I'm not sure the extra complexity is worth it, unless you can slot in a modified syntax for rules that says "don't report this one." There's already seemingly a use case for only warning on a violation (the alternate header name), so having a way to put in modifiers like this could be useful in other places too.
>
> something like (using ABNF):
>
>    source = [ modifier ]  "self" / scheme ":" host  [ ":" port ]
>    modifier = "^" // do not report
>                    / "?"  // warn only
>
> etc.
>
>
>>> - I tried adding a X-WebKit-CSP header with the same policy on the front page, but Chrome behaved differently; e.g., it didn't want to load a local .js, even though that's allowed by the policy.
>>
>> If you send me a reduced test case of the issue you're running into,
>> I'd be happy to fix it.
>
> Will see what I can do.
>
> --
> Mark Nottingham   http://www.mnot.net/

Received on Tuesday, 26 July 2011 16:25:24 UTC