Open TomiBelan opened 4 years ago
There was an email thread at Google about this in early 2018. It didn't reach a conclusion (everyone was too busy at the time). @koto prompted me to finally file this issue about it.
I'm pasting the thread here, to continue the discussion and maybe to refresh people's memory. I removed some details about the Google project where this issue came up (it's not public). Ping me on corp if you need details about the use case.
I tend to agree that trying to enforce cross-document consistency when documents (can) have different policies is not a good use of time.
cc @domenic
Is there any browser that blocks unsafe evaluation when just a single frame is involved?
test.html
:
<meta http-equiv="content-security-policy" content="script-src 'unsafe-inline'">
<script>
hash=location.hash.substring(1);
malicious='javascript:' + hash;
location.href=malicious;
//eval(malicious);
</script>
When I open this page in Chrome file:///Users/.../test.html#alert(malicious)
, the dynamic script built from the URL fragment is evaluated.
@dinofx, I think that is not related to this issue. Your test.html is working as intended. According to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src, 'unsafe-inline'
allows javascript:
URLs. If you want to forbid navigation to javascript:
, don't use 'unsafe-inline'
. See also https://w3c.github.io/webappsec-csp/#directive-script-src.
Given the discussion, and since vendors are already checking only calleeRealm (see #539), I think it makes sense to change the spec to do the same.
I already wrote tests, I'll draft a PR.
Will you also change JS and HTML? (This also came up in the Wasm discussion.)
I think technically no changes to JS and HTML are required. CSP would just ignore one of the arguments those specs pass. Which seems reasonable-ish.
The rules of cross-frame string compilation have some counter-intuitive results.
"If a parent frame forbids 'unsafe-eval' and a child frame allows 'unsafe-eval', and both are on the same origin, should
childIframeElement.contentWindow.eval('foo')
be allowed?"The CSP spec says no. EnsureCSPDoesNotBlockStringCompilation at https://w3c.github.io/webappsec-csp/#can-compile-strings says that both callerRealm and calleeRealm must allow 'unsafe-eval'. (callerRealm is from the second to top element of the execution context stack, i.e. the parent iframe. calleeRealm is from eval's context, i.e. the child iframe. See https://tc39.es/ecma262/#sec-eval-x.)
I guess the intention of this rule might've been to force the iframes to intentionally cooperate in this scenario. For example, maybe the idea was to allow it only if the child iframe explicitly defines a global function that calls eval, and the parent iframe calls that global function, instead of calling contentWindow.eval directly.
However, the child iframe's cooperation is not required. The parent can run e.g.
f.contentWindow.Array('foo').map(f.contentWindow.eval)
, and this is allowed by the spec, because the second to top stack element is the child iframe's native map method.I don't think that it makes sense that
f.contentWindow.eval('foo')
is forbidden butf.contentWindow.Array('foo').map(f.contentWindow.eval)
is allowed. If an XSS-vulnerable page can be tricked into running one with untrusted user input, I think it's likely that it can also be tricked into running the other. (Side note: a script gadget of this strength might just be able to run anything anyway, with createElement + append, especially if 'strict-dynamic' is also allowed.)There are two possible directions for resolving this:
f.contentWindow.eval('foo')
andf.contentWindow.Array('foo').map(f.contentWindow.eval)
. I.e.: stop checking callerRealm, check only calleeRealm. This makes the most sense IMHO. (FWIW, all current browsers implement it this way at the moment, not following the spec.)f.contentWindow.eval('foo')
andf.contentWindow.Array('foo').map(f.contentWindow.eval)
. This might be ugly. Say hello to incumbent realm?In other words: if the callerRealm restriction is pointless, it should be removed. If it is not pointless and fulfills an important purpose (which I can't see), it should be strengthened.