Re: Comments on the Content Security Policy specification

On Thu, 16 Jul 2009, Bil Corry wrote:
> Ian Hickson wrote on 7/16/2009 5:51 AM: 
> > I think that this complexity, combined with the tendency for authors 
> > to rely on features they think are solvign their problems, would 
> > actually lead to authors writing policy files in what would externally 
> > appear to be a random fashion, changing them until their sites worked, 
> > and would then assume their site is safe. This would then likely make 
> > them _less_ paranoid about XSS problems, which would further increase 
> > the possibility of them being attacked, with a good chance of the 
> > policy not actually being effective.
> 
> I think your point that CSP may be too complex and/or too much work for 
> some developers is spot on.  Even getting developers to use something as 
> simple as the Secure flag for cookies on HTTPS sites is still a 
> challenge.  And if we can't get developers to use the Secure flag, the 
> chances of getting sites configured with CSP is daunting at best.

I agree. I think many people will try, will think they got it right 
(because their site works), and will then assume that they therefore don't 
have to worry about (e.g.) people inserting scripts into their pages, when 
in fact they just allowed anything.


> At first glance, it may seem like a waste of time to implement CSP if 
> the best we can achieve is only partial coverage, but instead of looking 
> at it from the number of sites covered, look at it from the number of 
> users covered.  If a large site such as Twitter were to implement it, 
> that's millions of users protected that otherwise wouldn't be.

Assuming they got it right.

I think that something like CSP can definitely be useful. I just think it 
has to be orders of magnitude simpler.


> > I think CSP should be more consistent about what happens with multiple 
> > policies. Right now, two headers will mean the second is ignored, and 
> > two <meta>s will mean the second is ignored; but a header and a <meta> 
> > will cause the intersection to be used. Similarly, a header with both 
> > a policy and a URL will cause the most restrictive mode to be used 
> > (and both policies to be ignored), but a misplaced <meta> will cause 
> > no CSP to be applied.
> 
> I agree.  There's been some discussion about removing <meta> support 
> entirely and/or allowing multiple headers with a intersection algorithm, 
> so depending on how those ideas are adopted, it makes sense to ensure 
> consistency across the spec.

Removing <meta> altogether would be one good step towards simplification.


On Fri, 17 Jul 2009, Daniel Veditz wrote:
> Ian Hickson wrote:
> > This isn't intended to be a "gotcha" question. My point is just that 
> > CSP is too complicated, too powerful, to be understood by many authors 
> > on the Web, and that because this is a security technology, this will 
> > directly lead to security bugs on sites (and worse, on sites that 
> > think they are safe because they are using a Security Policy).
> 
> So do you have a simpler syntax to suggest? A different approach 
> entirely?

Here are some suggestions for simplification:

 * Remove external policy files.
 * Remove <meta> policies.
 * If there are multiple headers, fail to fully closed.
 * Combine img-src, media-src, object-src, frame-src
 * Combine style-src and font-src
 * Drop the "allow" directive, default all the directives to "self"
 * Move "inline" and "eval" keywords from "script-src" to a separate 
   directive, so that all the -src directives have the same syntax



> Or should we do nothing and expect site authors to write correct and 
> safe PHP+HTML+JavaScript as it stands. CSP seems far less complicated 
> than the things authors already are expected to understand.

Authors get the things authors already are expected to understand wrong 
all the time.


> >>>    X-Content-Security-Policy: allow https://self:443
> >> Using "self" for anything other than a keyword is a botch and I will 
> >> continue to argue against it.
> > 
> > The examples I gave in the previous e-mail were all directly from the 
> > spec itself.
> 
> The spec is a group effort and I'm sure there are things in it each of
> us would prefer to be different. It's also not set in stone, which is
> why I mention things like this (but I don't hear a lot of agreement so
> maybe everyone else likes using "self" as a pseudo-host).

My point is that these are not things I made up -- they are policies that 
have been put forward by people as examples. If they demonstrate problems, 
then it's not just me making up edge cases that show problems.


> >> I'll admit that the default "no inline" behavior is not at all 
> >> obvious and people will just have to learn that
> > 
> > This strategy has not worked in the past.
> 
> But in this case they will learn rather quickly if their site doesn't 
> work.

I'm concerned that people will eventually do something that causes the 
entire policy to be ignored, and not realise it ("yay, I fixed the 
problem") or will do something that other people will then copy and paste 
without understanding ("well this policy worked for that site... yay, now 
I'm secure").


> >> We are not creating this tool for naive, untrained people.
> > 
> > Naive, untrained people are who is going to use it.
> 
> Yes, but we're really trying to protect the millions of users who visit 
> Google, Yahoo, PayPal, banks, etc, and hopefully those kinds of 
> high-traffic sites are run by smart people (yes, I am being naive).

It doesn't matter who you are trying to protect. This _will_ be used by 
naive, untrained people, and so we have to make sure it works for them.


> > I agree entirely. But we don't get to require that people pass a test 
> > before they use a technology. They'll use it because they heard of it 
> > on w3schools, or because someone on digg linked to it, or because 
> > their friend at the local gym heard his sysadmin team is using it.
> > 
> > We know that people do this. We have to take that into account.
> 
> I don't know what to do with this feedback. Are you saying "don't do 
> CSP"? Do you have suggestions on how to make it safer or simpler to use? 
> An alternate technology that will address the XSS problem?

I think massive simplification would be a start; I'm not sure how much 
further we would have to go after that.


> > I would recommend making the entire policy language signficantly 
> > simpler, such that it can be expressed in less space than a URL's 
> > length, which would solve this problem as well as the above issues.
> 
> Since the policy is mostly a list of hosts or domains it would seem 
> difficult to shorten it much. We could make the directives terse or even 
> cryptic, but that doesn't gain much in length nor would it help 
> understandability.

We could remove many of the directives, for example. That would make it 
much shorter.


> >> It will block page _parsing_, just as a <script> tag must (except, of 
> >> course, before parsing starts).
> > 
> > I think that would basically make the external policy unusable for 
> > Google properties. Specifying a policy inline would still be ok 
> > though.
> 
> Using a policy file and having a different one for every page would be 
> horrid, but what would be the problem with having a cachable policy file 
> per service? Only the user's initial visit would suffer.

Making the user's initial visit suffer wouldn't be acceptable to Google, 
for several reasons; first, it seems that far more visits than just the 
"initial" visit involve cache misses, and second, the first visit is the 
most important one in terms of having a good (= fast) user experience.


On Fri, 17 Jul 2009, Brandon Sterne wrote:
> 
> Dan's point is absolutely true.  The majority of sites will be able to
> benefit from simple, minimal policies.  If a site hosts all its own
> content then a policy of "X-Content-Security-Policy: allow self" will
> suffice and will provide all the XSS protection out of the box.

It will also break inline scripts, analytics, and ads.


> I tend to think this will be the common example that gets cut-and-pasted 
> the majority of the time.

I doubt it, because of what it breaks.


> Content Security Policy has admittedly grown more complex since it's
> earliest design but only out of necessity.

I strongly recommend reconsidering what is necessary.


> I don't think it makes sense for sites to work backwards from a complex
> policy example as the best way to understand CSP.

I don't think it makes sense either. Yet that's what people do with HTML, 
CSS, and JS. Why would it be any different here?


> I imagine sites starting with the simplest policy, e.g. "allow self", 
> and then progressively adding policy as required to let the site 
> function properly.  This will result in more-or-less minimal policies 
> being developed, which is obviously best from a security perspective.

This is maybe how competentely written sites will do it. It's not how most 
sites will do it.


> >> data URLs? nope, not mentioned
> >> inline handlers? nope, not mentioned
> > 
> > How is an author supposed to know that anything not mentioned won't work?
> > 
> > And is that really true?
> > 
> >    X-Content-Security-Policy: allow *; img-src self;
> > 
> > Are cross-origin scripts enabled? They're not mentioned, so the answer 
> > must be no, right?
> > 
> > This isn't intended to be a "gotcha" question. My point is just that CSP 
> > is too complicated, too powerful, to be understood by many authors on the 
> > Web, and that because this is a security technology, this will directly 
> > lead to security bugs on sites (and worse, on sites that think they are 
> > safe because they are using a Security Policy).
> 
> I don't think your example is proof at all that CSP is too complex.  If
> I were writing that policy, my spidey senses would start tingling as
> soon as I wrote "allow *".  I would expect everything to be in-bounds at
> that point.  This is a whitelist mechanism after all.

Ok, consider:

   X-Content-Security-Policy: allow self;

Will cross-site form submission work? It's not mentioned, so the answer 
must be no, right? As far as I can tell, the answer is actually yes.

You are assuming the person reading all this is familiar with security 
concepts, with Web technologies, with "whitelists" and wildcards and so 
on. This is a fundamentally flawed assumption.


> >>> This would then likely make them _less_ paranoid about XSS problems,
> >>
> >> I hope not, since it does nothing to help their visitors using legacy 
> >> browsers that don't support CSP.
> > 
> > That's a temporary situation. In 20 years, when everyone supports it 
> > and nobody cares about today's browsers, CSP will make people less 
> > paranoid.
> 
> It is possible that is the case, but I don't think it is justifiable to 
> not provide tools because we are worried that people will come to rely 
> upon them for security.  An analogy: seat belts were introduced in the 
> auto industry and yet people still (attempt to) drive safely even though 
> they know they're safely buckled-up.  Industry reliance upon a anti-XSS 
> mechanism such as CSP is a problem I would be happy to have.

Seatbelts are simple to understand. Make CSP as simple as seatbelts and 
I'll agree.


> >>> I don't think UAs should advertise support for this feature in their 
> >>> HTTP requests. Doing this for each feature doesn't scale.
> >>
> >> I personally agree for all the reasons you mention, but we still have 
> >> a potential versioning problem to resolve. Or not -- if we do nothing 
> >> we could always add a CSP-2 header in the future if necessary. I'm 
> >> just worried that it's unlikely that we thought of everything the 
> >> first time through.
> > 
> > Just make sure it's forwards-compatible, so you can add new features, 
> > then you don't need to version it. (The same way HTML and CSS and the 
> > DOM have been designed, for instance.)
> 
> I think Dan summarized the trade-off nicely here: 
> http://groups.google.com/group/mozilla.dev.security/msg/787c87362d08bf5e
> 
> I can see why folks want to avoid a version string but several of us 
> have limited confidence in our ability to design with 
> forward-compatibility.  Perhaps you could provide some guidance in this 
> particular area since you have a lot of experience doing so.

Make the BNF that defines the syntax be something that matches all 
possible strings.

For example, here is a trivial syntax like that:

   <list>              ::= "" | <directive> | <directive> ";" <list>
   <directive>         ::= <name> | <name> ":" | <name> ":" <value>
   <name>              ::= anything but ":" or ";"
   <value>             ::= anything but ";"

Then, extend this so that it covers the syntax you want to support, and 
leaves the invalid stuff in productions with "invalid" in the name, e.g.:

   <list>              ::= "" | <directive> | <directive> ";" <list>
   <directive>         ::= <valid-directive> | <invalid-directive>

   <valid-directive>   ::= <color> | <smell>
   <color>             ::= "color" ":" <color-values> <invalid-extra>
   <color-values>      ::= "red" | "green"
   <smell>             ::= "smell" ":" <smell-values> <invalid-extra>
   <smell-values>      ::= "flower" | "honey"

   <invalid-extra>     ::= <value>

   <invalid-directive> ::= <name> | <name> ":" | <name> ":" <value>
   <name>              ::= anything but ":" or ";"
   <value>             ::= anything but ";"

Then, define that the UA has to parse all input strings into the tree of 
directives that it maps to.

So for example, with the above syntax, "foo:bar" would parse to:

   list
    +- directive
        +- invalid-directive
            +- name
            |   +- "foo"
            +- ":"
            +- value
                +- "bar"

...and "color:red blue" would parse to:

   list
    +- directive
        +- valid-directive
            +- color
                +- "color:"
                +- color-values
                |   +- "red"
                +- invalid-extra
                    +- value
                        +- " blue"

...then, define how you walk this tree. Typically, you would say that you 
ignore any directives that have "invalid" in their name, so the examples 
above would be treated as "" and "color:red" respectively.

This then allows you to extend the syntax in both directions -- ignoring 
new bits, and ignoring entire directives. For example, "color:red except 
orange" would be treated as "color:red" in v1 UAs, whereas 
"color:excluding orange from red" would be treated as "".

So, let's look at the example Dan gave. Today, to allow scripts in the 
head, you need:

   script-src: inline;

If we ever add a keyword to make only scripts in the head work:

   script-src: head;

...legacy UAs will end up stripping the scripts you wanted kept, thus 
causing a backwards-compatibility problem. If you design it with the model 
above, you could introduce the feature as the following instead:

   script-src: inline only head;

Legacy UAs would see just "inline", new UAs would see both.

HTH,
-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

Received on Wednesday, 29 July 2009 22:24:32 UTC