Finalizing the shape of CSP ‘unsafe-dynamic’

Hey all,

Based on the discussion in the F2F and subsequent conversations with cc'ed
folks it seems that most people think that ‘unsafe-dynamic’ [1,2] is a
reasonable idea. At the same time, some aspects of the current proposal
(e.g. the name itself) could probably be improved; I’d like to use this
thread to iron out any remaining bits.

<recap>
‘unsafe-dynamic’ is meant to enable the creation of policies that use CSP
nonces/hashes to allow script execution instead of the current origin-based
source whitelists, which are almost always bypassable by attackers [3].
‘unsafe-dynamic’ makes adopting nonce-based policies easier by allowing
already executing, trusted scripts to dynamically include additional
scripts via non-parser-inserted APIs [4], without requiring developers to
manually pass around the nonce. Additionally, it makes the user agent
ignore the script-src whitelist to allow the creation of secure
backwards-compatible policies -- older browsers can keep using the
whitelist but supporting browsers will rely solely on nonces.

In such a nonce-based approach the developer would specify a policy with a
script-src containing a nonce and ‘unsafe-dynamic’, and make sure all
<script> elements in the response have a valid `nonce’ attribute -- in many
applications this can be much simpler (and more secure) than a
whitelist-based policy. So far we’ve had very promising results using this
approach with popular JS widgets [5] and in several Google applications,
and are working on much broader adoption of this way of using CSP.
</recap>

The main concerns that folks have brought up are that the name doesn’t
match the goal of the feature (a policy with ‘unsafe-dynamic’ will be safer
because it won’t be vulnerable to whitelist bypasses, and the ‘unsafe-’
prefix could hinder adoption), and that it makes script-src more difficult
to reason about. A policy with ‘unsafe-dynamic’ might specify
‘unsafe-inline’ and a host whitelist for backwards compatibility, and
depending on the level of the user-agent’s CSP support they will sometimes
be respected and sometimes ignored.

Overall, I think we have the following options:

1. Leave ‘unsafe-dynamic’ as-is, name and everything.
2. Change the name, but otherwise leave the behavior as specified. Some
possibly better names that have been thrown around were ‘allow-dynamic’,
‘trust-dynamic’, ‘dynamic-nonce’ and my favorite ‘safe-dynamic’ ;-) (okay,
maybe the last one won’t fly)
3. Change the behavior to make the feature more understandable to
developers. Ideas for this included introducing another directive (rather
than using script-src), or splitting ‘unsafe-dynamic’ into two keywords
(e.g. ‘drop-whitelist’ and ‘propagate-nonces’) -- each of those comes with
its own set of challenges, but they are probably solvable if it seems like
this is the way to go.


My choice would be #2, i.e. to change the name, which AFAIK was the only
strong concern raised in our discussions, but otherwise leave the behavior
as currently written in the spec. But if anyone feels strongly about #3 or
has ideas for a cleaner way to accomplish the same goal, it would be great
to discuss it now.

Cheers,
-Artur

[1] https://w3c.github.io/webappsec-csp/#unsafe-dynamic-usage
[2] https://lists.w3.org/Archives/Public/public-webappsec/2016Feb/0048.html
[3]
https://github.com/cure53/XSSChallengeWiki/wiki/H5SC-Minichallenge-3:-%22Sh*t,-it's-CSP!%22
In a recent review of CSP deployments in the wild we observed that 75% of
whitelists contain origins which allow such CSP bypasses because of
Angular/JSONP endpoints. As a result, >95% of current CSP policies offer no
benefit from markup injection/XSS.
[4] https://www.w3.org/TR/html5/scripting-1.html#parser-inserted
The reason to allow only non-parser-inserted APIs to be in scope of
‘unsafe-dynamic’ is that parser-inserted APIs (Element.innerHTML,
document.write()) tend to suffer from XSS, which would allow attackers to
still exploit such bugs.
[5] https://csp-experiments.appspot.com/unsafe-dynamic

Received on Thursday, 2 June 2016 12:27:34 UTC