-
Notifications
You must be signed in to change notification settings - Fork 149
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
CSP and HTML Modules #544
Comments
That's still the same basic problem though, no? If you find some kind of XSS (granted, it's more involved as there's multiple steps) an attacker gets to influence that. I guess the main question if we see these as subresources that are similar to other subresources, or subresources prone to XSS due to their HTML-y nature. |
(FWIW, script-src makes sense, and I don't think CSP policies delivered with a module script should apply as they don't for "normal" module scripts either.) |
I think HTML Modules isn't prone to XSS. Because it's not meant to dynamically generate HTML according to user input. Yes, there might be a DOM based XSS depending on inline scripts, but that's same as subresources like JS file. |
There is some precedent for this in how HTML imports interacted with CSP. AFAIK they applied the loading document's The main concern here is that HTML is frequently constructed unsafely, with user data interpolated without sufficient escaping; we can hope that HTML modules will be static as @shhnjk says, but there is no reason an application couldn't use user-controlled data when returning the contents of the module, leading to an XSS that wouldn't be subject to CSP, resulting in a security regression. OTOH we can also treat this more similarly to ES modules and assume that, once loaded, the module gets the ability to execute scripts -- I don't think that's necessarily a wrong way to look at this. No concerns from me when it comes to inheriting the loading document's CSP. One thing to keep in mind in this model is that we should tell developers to always also set a CSP on the HTML module response. An attacker can navigate the victim to it directly so if there's a DOM XSS in a script in the HTML module, it would execute in the context of the hosting origin (which is difference between HTML modules and ES modules / JS subresources); developers would need to have CSP set on such responses even if the policy would normally get ignored. |
I understand this might be a stale issue, but I found a different behavior in how Chrome, Safari and Firefox interact with HTML Modules and CSP. Maybe someone can help me better understand it. We had an inline script importing a module: <script type="module">
import start from './start.js';
start();
</script> We used sha256 hashing for our CSP tag: <meta http-equiv="Content-Security-Policy" content="
script-src 'unsafe-eval' 'unsafe-inline' https: 'strict-dynamic' 'sha256-GD2MyhNzRFjmxD4jAUnvYIt90C1er46lTotfFdaZ3lg='
"> I found three different behaviors: Safari: It works. Chrome:
Yet, if we preload the module
Firefox: It doesn't work in any case. We had to do a workaround: We moved the inline script to a file: // main.js
import start from './start.js';
start(); Then we loaded it dynamically in the html: <script>
const loader = document.createElement("script");
loader.type = "module";
loader.src = "./main.js";
document.head.appendChild(loader);
</script> We had to change the sha256 of the CSP to the new inline script. Any ideas of why this different behavior? Thanks!! |
This is confusing, but I believe chrome is following the specification in the example above. I think the specification prescribes that the static imported script should be fetched with the same options as the parent script. Hence, the imported script is fetched as parser-inserted, hence
then chrome loads I don't know the background, but this makes me wonder whether we should actually consider statically imported scripts as non-parser-inserted, so that As for |
I actually think his may be a browser bug related to using hashes for module scripts. If you switch the example above to use a nonce (so add AFAIK this is orthogonal to |
@arturjanc I think what you are writing confirms my previous comment. As I wrote, the spec prescribes the descendants of a module script to be fetched with the same options as the parent. If the parent has a nonce, the descendants will be fetched with the same nonce stored in the fetch client settings object, and the CSP spec check will succeed because it finds a matching nonce. Hence I believe the bug is actually in the specification. If we think that static imports should not require anything more to load (i.e. if the parent is allowed to load, then all descendants should automatically be allowed to load), then we should change the spec. |
Thanks for the comments!! Not that I fully understand it, but it kind of makes sense. Correct me if I'm wrong:
Thanks again! |
My colleagues and I on the Microsoft Edge team recently shared a proposal for HTML Modules as an extension of ES6 JavaScript modules. HTML Modules allow for access to declarative content from within a script module. Our Explainer Doc fleshes out this idea.
One developer pointed out that our use of inline scripts in HTML Modules could interact poorly with CSP’s
unsafe-inline
source expression.An HTML Module uses inline scripts for two primary purposes: to specify its exports, and to import other modules (script or HTML) that it depends on. The reason that inline scripts in particular are required is that a non-inline module script can be imported from multiple contexts, making its referrer document ambiguous, so it’s not clear how a non-inline script would interact with its referrer to specify exports of HTML content.
An HTML Module’s inline scripts are different from normal scripts in a few ways. Firstly, only
type=”module”
scripts are allowed, so there’s no synchronous execution during parsing. Secondly, their execution isn’t queued up by the parser as per normal deferred scripts. Instead, they are incorporated into the module graph as the required modules of the HTML Module, similarly to how a script module’s required modules consist of the set of modules requested in its all of itsimport {…} from “…”
statements. As such they are executed only after the entire module dependency graph has been fetched, as part of the module graph evaluation when all the modules in a module graph are executed in order of a depth-first post-order traversal.So, inline scripts in HTML Modules don’t have the same behavior as normal inline scripts. Another way to think about them is that they are declaratively specified script module imports of the HTML module. Additionally, it's impossible to dynamically inject a script into a module in such a way that it will execute; only scripts that were parsed with the original HTML Module are included in the module graph and executed.
Given the above, we believe that inline script elements included in HTML Modules should be allowed to run even without the presence ‘unsafe-inline’ in a script-src directive.
This decision clearly merits input from the experts. Does anyone in the WG have concerns with this? Are there any dangers here that we are missing?
Also worth noting is that we would propose to have the importing of HTML Modules governed under the script-src directive in the same way that it applies to importing of script modules.
One more question we're considering is whether or not an HTML module (which is a document) applies CSP directives specified in its headers when it is being processed as part of the module graph. We're thinking the answer is no -- that only the "root" document's CSP directives apply -- but we're not sure. On the one hand, if I want to consume a module hosted on a third-party server I shouldn't have to worry about whether the server’s lax CSP policies could compromise the security of my application. On the other hand, it seems unintuitive that a text/html response’s CSP is respected or not based on whether it’s getting pulled in as a module vs. loaded as a top-level document. Feedback on this is welcome as well.
We’ve also been considering the issue here regarding module imports in a nonced inline script, but this seems more applicable to ES6 modules in general with no special consideration needed for HTML Modules.
The text was updated successfully, but these errors were encountered: