- From: Nick Gearls <nickgearls@gmail.com>
- Date: Tue, 26 Jul 2011 12:27:43 +0200
- To: public-web-security@w3.org
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