Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does the meta http-equiv="Content-Security-Policy" tag allow lists of policies? #5102

Open
bakkot opened this issue Nov 22, 2019 · 10 comments
Open
Labels
impacts documentation Used by documentation communities, such as MDN, to track changes that impact documentation interop Implementations are not interoperable with each other needs tests Moving the issue forward requires someone to write tests security/privacy There are security or privacy implications

Comments

@bakkot
Copy link
Contributor

bakkot commented Nov 22, 2019

The html spec says that the content of an <meta http-equiv="Content-Security-Policy" content="..."> tag should be a serialized-policy and should be parsed according to Parse a serialized CSP.

This implies that it does not allow multiple comma-separated policies, such as img-src 'none', script-src 'sha256-lLvWePLrgCn07EcwYB0JPy65n3OloEYiWK34Ql9Zdmc='. That would be a serialized-csp-list, parsed according to Parse a serialized CSP list, which nothing I can find actually uses. (Also, that algorithm returns multiple policies, each of which would need to be enforced.)

However, both Chrome and Safari do allow policy lists such as the above. Firefox and the Nu HTML checker do not - Firefox attempts to parse it as a single policy including the ,, while the Nu HTML checker considers it an outright error.

As far as I can tell, the web platform tests do not cover this case.

Here is a simple page with the above comma-separated CSP, which contains both an image and an inline script with no hash. Its source is below.

Which is the intended behavior?

demo page source
<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="Content-Security-Policy" content="img-src 'none', script-src 'sha256-lLvWePLrgCn07EcwYB0JPy65n3OloEYiWK34Ql9Zdmc='">
<title>CSP test</title>
</head>
<body>
  <span id="title">If you can see this, the CSP violation event listener was prevented from executing.</span>
  <script nonce="example">
    document.getElementById('title').innerText = 'CSP directives violated:';
    let directives = new Set;
    document.addEventListener('securitypolicyviolation', function(e) {
      directives.add(e.violatedDirective);
      document.getElementById('violations').innerHTML = [...directives].map(v => '<li>' + v).join('');
    });
  </script>
  <ul id="violations"></ul>

  <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="a red dot">
  <script>console.log('a console log from an inline script')</script>
@annevk annevk added interop Implementations are not interoperable with each other needs tests Moving the issue forward requires someone to write tests security/privacy There are security or privacy implications labels Nov 22, 2019
@annevk
Copy link
Member

annevk commented Nov 22, 2019

cc @mikewest @ckerschb

@sideshowbarker
Copy link
Contributor

However, both Chrome and Safari do allow policy lists such as the above. Firefox and the Nu HTML checker do not - Firefox attempts to parse it as a single policy including the ,, while the Nu HTML checker considers it an outright error.

The Nu Html Checker just uses https://github.com/shapesecurity/salvation/ on the backend, as https://cspvalidator.org/ also does. So I reckon @shekyan might also be interested in clarification on this

@bakkot
Copy link
Contributor Author

bakkot commented Dec 28, 2019

Salvation (which I help maintain) actually has APIs for parsing both single policies and lists of policies. The fact that the Nu HTML checker disallows lists means they've chosen to use the API for a single policy, I believe.

@sideshowbarker
Copy link
Contributor

sideshowbarker commented Dec 28, 2019

Explanation of why the HTML checker doesn’t allow lists of policies

Salvation (which I help maintain) actually has APIs for parsing both single policies and lists of policies.

aha

com.shapesecurity.salvation.Parser.parse vs com.shapesecurity.salvation.Parser.parseMulti

The fact that the Nu HTML checker disallows lists means they've chosen to use the API for a single policy, I believe.

The they in this case is me. I never intentionally chose to explicitly limit the checker to using the API for a single policy. I just did it out of laziness/ignorance.

…or else I guess possibly salvation back then might not have yet had support for checking a list of policies, at the time I initially added in the feature — ~4 years ago, circa salvation-1.0.3; see validator/validator@dfd77df#diff-e706d1dfdc30b96b123a2fa3cb7eadacR66, which just directly calls com.shapesecurity.salvation.Parser.Parse(policy).

Salvation (which I help maintain) actually has APIs for parsing both single policies and lists of policies.

OK then, if/when the HTML spec is updated to clearly allow lists of policies, I could happily and easily update the HTML checker code to allow lists of policies.

@sideshowbarker sideshowbarker added the impacts documentation Used by documentation communities, such as MDN, to track changes that impact documentation label Dec 28, 2019
@mikewest
Copy link
Member

As you note, Chrome (and presumably WebKit, since the implementation is likely common to both) simply takes whatever's in the <meta> tag and throws it into the same parser that's used for HTTP-delivered policies. Commas in the one have the same effect as commas in the other.

I'm surprised to hear that that comes up in practice, as I don't think there's much value in delivering multiple policies via <meta>, but I also don't have a particular reason to prevent that behavior from happening. It seems reasonable to treat http-equiv somewhat literally: the tag's content is the equivalent of text delivered from headers, and treating it the same seems reasonable.

I suspect I intended to link to "parse a serialized CSP list" when I sent the PR against HTML. That would create the behavior I suggested above.

If y'all agree with the rationale, I'll send that patch and add some tests.

@bakkot
Copy link
Contributor Author

bakkot commented Jan 17, 2020

as I don't think there's much value in delivering multiple policies via <meta>

There is considerable value in it because there are behaviors which are not expressible as a single policy, only as a list of policies. (The simplest example is two policies with different report-to, but consider also script-src 'nonce-value', script-src example.com, which requires that scripts be from example.com and have the appropriate nonce.)

That might not come up if you're writing a policy manually, but it certainly does if you're trying to automatically manipulate policies. (That's how I ran into it.)

If y'all agree with the rationale, I'll send that patch and add some tests.

SGTM

@mikewest
Copy link
Member

There is considerable value in it because there are behaviors which are not expressible as a single policy, only as a list of policies.

I agree that multiple policies are valuable! I'm questioning the value of that mechanism in <meta> specifically. report-to isn't supported at all via <meta>, and putting nonces inline is a great way for them to be stolen and/or abused by script on the page. Hashes are likely safe, but are also not terribly widely-used.

Still, I'd prefer to align on Chrome's behavior. I'll send a PR to that effect.

@annevk
Copy link
Member

annevk commented Jan 17, 2020

There's going to be parser input differences at least as one is a byte sequence and the other is a string.

I would also expect combining differences. Ideally for HTTP we combine first and then parse, but I don't think that's workable for http-equiv as we cannot wait for the last one to arrive.

@bakkot
Copy link
Contributor Author

bakkot commented Jan 17, 2020

There's going to be parser input differences at least as one is a byte sequence and the other is a string.

That comes up already; browsers seem to just reject any http-equiv="Content-Security-Policy" whose content cannot be interpreted as ASCII. (I can't quite tell if that's per spec because the spec is unclear about what the specified behavior should be, but it seems reasonable.)

Ideally for HTTP we combine first and then parse, but I don't think that's workable for http-equiv as we cannot wait for the last one to arrive.

There would still be a maximum of one http-equiv="Content-Security-Policy" tag, so I don't think that would be an issue. It's just that the content of such a tag could contain a list of policies rather than just one.

@sideshowbarker
Copy link
Contributor

Not sure what the next step is here…

Still, I'd prefer to align on Chrome's behavior. I'll send a PR to that effect.

@mkwest are you still planning on doing that, or did @annevk‘s comment at #5102 (comment) dissuade you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
impacts documentation Used by documentation communities, such as MDN, to track changes that impact documentation interop Implementations are not interoperable with each other needs tests Moving the issue forward requires someone to write tests security/privacy There are security or privacy implications
Development

No branches or pull requests

4 participants