CSP: update modal-chooser so that it loads js source from URL using <script> rather than using eval()
See original GitHub issuethe modal-chooser currently uses a mechanism that loads modal HTML+JS from a single URL as json response, then extracts the HTML as raw string and injects it into the page, and then extracts the JS as raw string and turns it into executable code using eval()
.
The first part might be dangerous, because it might lead to injecting user-generated html (note: I’m not sure, didn’t look deeply enough into it), but the second part violates CSP something fierce, so seeing if it’s possible to rework this a little to load the associated javascript as a dedicated URL output using a <script>
tag so that sensible CSP settings don’t break wagtail would be a good idea.
I’m having a quick look at this focussing specifically on the images/chooser use case:
- leave the images/chooser route as is
- add an images/chooser/js route for exclusively serving the JS content with the correct mime-type
- keep the modal-chooser as extracting and injecting HTML
- change the modal-chooser to load the JS by instead creating a
<script src="..."></script>
and adding that to the document<head>
That might still trip up CSP, but hopefully it doesn’t.
Issue Analytics
- State:
- Created 6 years ago
- Comments:8 (6 by maintainers)
Top GitHub Comments
While I agree that doing the right thing is more important than “following the rules”, production installs by companies like Mozilla simply can’t be set up if
unsafe-eval
is necessary, so there’s a middle ground here where I can write this PR that first makes the code “do what it does, just a little differently, without violating CSP” so that wagtail can be deployed while ticking the boxes (fairly literally: there is a security anlysis review with checkboxes that in fact do need ticking before something can go live =), which at least improves the CSP violations for people right now, followed by a proper bigger rewrite that makes wagtail serve as much static JS up front as possible instead of sending it over call-by-call.I personally dislike CSP but the reality is that a lot of companies rely on it, and cannot deploy services without a strong CSP in place (and without the freedom to run the /admin routes on a completely different domain), so if that’s a reason they can’t use wagtail that feels like a good thing to first fix “so things work” followed by a second fix “so that things work really well”.
At the very least,
eval()
is never a good thing, and rewriting the code a bit so that rather thaneval()
, a normal script element is built, should sidestep the CSP violation errors with relatively minimal impact to the current state of the code. That’ll be a smaller PR than what I suggested above, so I’ll see about filing that first.(I’m having a bit of difficulty getting things set up for dev as recommended by the docs - vagrant is not even remote cooperating with me right now)
Hi @Pomax,
If we’re going to try to follow CSP rules, I’m keen that we should respect the intent of those rules (i.e. “don’t serve dynamically-constructed JS code”) rather than treating it as a box-ticking exercise (“don’t use
eval
”). I’m concerned that the approach proposed here isn’t doing that: we’re still sending dynamically-constructed JS responses (which may contain untrusted strings from user input) and just finding another way of evaluating them that avoidseval
. If anything, setting CSP headers without dealing with the underlying issue makes it more dangerous, since we’re claiming a level of “trustedness” for our JS code that we can’t really ensure. (I’m also worried that if we have to add hacks to make it work, such as stashing things in global variables between requests, then we’re adding more opportunities for things to break, rather than making the code more secure.)If a sysadmin / hosting provider is requiring the use of CSP, then rather than trying to sneak past the policy on a technicality, I think it’s more responsible for us to be upfront about it, and say “we can’t reasonably follow this policy in spirit; if our use of dynamic JS raises concerns about the security of the code, you should take alternative steps to mitigate it, such as putting the admin behind a separate locked-down domain”.
I think the modal views could (and should) be refactored to use a single fixed JS handler that receives pure JSON data on each step to tell it what to do next, rather than serving up a new dynamic JS handler on each step. (I know this is a bigger change than what you’re proposing, but I think it’s necessary as a step towards dealing with CSP ‘properly’, rather than working around it.)
More generally, my overall impression of CSP is that it assumes a model of application where the client-side behaviour is entirely fixed at build time, which doesn’t really fit well with how Wagtail is designed. Consider something like StreamField, where the client-side behaviour is determined by configuration that exists within Python code - we currently do that with “one-liner” calls to functions defined elsewhere. To do that the “CSP way”, we’d probably have to approach it in the same way as templating libraries (as described at https://developers.google.com/web/fundamentals/security/csp/#eval_too), and add a precompilation step. Obviously, that’ll be a major rewrite, and ultimately it may not be achievable - but I think any steps we take towards CSP support will have to be aimed in that direction.