-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
is mandating CORS for modules the right tradeoff? #1888
Comments
The web's fundamental security model is the same origin policy. We have several legacy exceptions to that rule from before that security model was in place, with script tags being one of the most egregious and most dangerous. (See the various "JSONP" attacks.) Many years ago, perhaps with the introduction of XHR or web fonts (I can't recall precisely), we drew a line in the sand, and said no new web platform features would break the same origin policy. The existing features need to be grandfathered in and subject to carefully-honed and oft-exploited exceptions, for the sake of not breaking the web, but we certainly can't add any more holes to our security policy. That's why, from our perspective, making module scripts bypass the CORS protocol (and thus the same-origin policy) is a non-starter. It's not about a specific attack scenario, apart from the ones already enabled by classic scripts; it's about making sure that new features added to the platform don't also suffer from the past's bad security hygiene. |
At Facebook we started using CORS long ago due to the advantages of getting stack traces. I agree with the principle here that script tags are basically just a non-declared CORS. I would add two insights:
|
Hmm, I don't understand this point very well. Web fonts require CORS. In many canvas-based scenarios (canvas being introduced around the same time as the line in the sand I mentioned earlier), images require CORS. Any static data files (e.g. .json) requested via fetch/XHR require CORS. HTML imports (RIP) required CORS. Etc.
Yeah, this is actually a perfect illustration of the issue. We want the SOP to protect you from reading and executing scripts from other peoples' public_html directories, but within your own, there's no issue at all.
This is interesting, but seems fairly orthogonal to the current discussion. FWIW, the spec defaults to credentials mode "omit" (same as Fetch), with the option of using |
The CORS requirement is also beneficial from an ecosystem perspective in that explicitly marking public content and requiring anonymous loads allows improvements in security and performance (like Subresource Integrity and content-addressable caching) that privacy concerns forbid without such explicit signals. |
What constitutes "new web platform features" here? Did These differences affect both the security calculus (can we actually close loopholes without changing/removing existing features?) and the adoption calculus (will people move from what they can currently do if the new thing reduces functionality?).
OK, that's a start: IOW, your answer to my first question is that this is about closing existing loopholes. But as I said, this doesn't actually close them since Put differently: with security considerations, we generally try to avoid the devil we don't know. But in this case, because of the existence of classic scripts, the security issues are the devil we know. It's the adoption risks that are the devil we don't know. In fact, I doubt this would be up for debate with |
The
Yes, as I stated, this is not about preventing existing attacks. It's about making sure that new features added to the platform don't also suffer from the past's bad security hygiene.
Precisely: if we were specifying script async today, it would fall clearly on the side of the line I mentioned, and we would require CORS for it. |
Really? I find this very surprising. |
Yep, really! Also, I forgot:
That's not really correct. New attacks are discovered on classic scripts all the time. Remember when people thought JSONP was safe, instead of its modern reputation as a huge XSS bug farm? Remember the great cross-origin Proxy debacle of 2015, which required changes in HTML/Web IDL/ES to address by locking down the prototype chain of all the global objects? (And even that only protected against the gaping hole of proxies; it doesn't protect against targeted attacks using getters, to which the web is still vulnerable.) The fact is that breaking the SOP breaks the fundamental protection we have on the web, and we're constantly scrambling to put the genie back in the bottle given classic scripts' failure to obey it. That's why we're not going to be breaking it for any future features going forward. |
Well, obviously I disagree. :) All I can say is, I don't find the line of reasoning of "there's a pre-existing pledge" as compelling rationale for anything.
You're not actually addressing my point, though. We can't actually close the loopholes without breaking web compat, because we can't break the web—as we all agree. So the loopholes exist, and now we're talking about standardizing a variation of the same feature. All I'm asking is that we engage with the practical adoption consequences, where the security consequences are already being dealt with and can't ever be eliminated. We can stand on a predetermined black-and-white principle and ignore the empirical consequences, but… reality bites! :) |
@dherman How is this any different than restricting things like brotli to https when gzip can be used on http? Clearly the better compression would get adopted faster if it was on http, but we don't want to take the risk vector. |
Without taking a position on the bigger issue, this seems like kind of an odd example given the known security issues with combining compression with HTTPS. Incidentally, what's the risk vector that you are concerned about with exposing Brotli to HTTP? |
@wanderview I don't know as much about compression and network protocols, nor do I have an opinion about whether that was the right call. But off the top of my head I imagine it's possible to enable brotli compression automatically in server software, without developers needing to change anything about how they develop their code. What we're talking about here is mixing two different programming models, where the new one doesn't supersede the old one. Often when new programming models are introduced that bring a new benefit but eliminate an old one, it can cause years of stagnated adoption, or even fail outright. |
As a web application and tooling developer (i.e. JSIL) I find this constraint extremely troubling, though the motivations behind it are good. The HTTPS requirements for new tech being applied by many browser vendors already impose some hurdles for amateur web developers and people maintaining old stacks, but at the very least this has dramatic upsides for users, so it's very easy to get behind it and direct force towards addressing ecosystem challenges that prevent every web service and server from supporting HTTPS. I think the same is not true for CORS. CORS is nontrivial to configure, nontrivial to interact with, and most importantly, extremely inconvenient to test locally in development scenarios. Many people still locally host their applications using things like python's httpd module for testing, especially since file:// is crippled in Popular Browsers. A CORS requirement for modules potentially requires amateurs to set up a whole server environment and figure out CORS just to test simple module-based applications. In the past I've worked with development environments at larger companies (30m+ users) that would also incur a significant burden if we had attempted to adopt a technology like this and introduce CORS everywhere. The web has moved forward so I'm sure it's easier now, but in the past when I attempted to use CORS (because I had to in order for my relatively simple application to work cross-browser) it was a struggle to find even one CDN with support, and it was difficult to get the configuration right so it would actually work. Testing CORS in local debugging scenarios was basically impossible. It's also not clear how users in shared-hosting scenarios would (as @bmaurer points out) actually configure CORS in their use cases - it would require some sort of local configuration file (.htaccess etc) in order to provide appropriate CORS headers for each resource. It's possible some web servers are not capable of doing this so now we would actually need new server logic to go through development, testing, and package manager updates. As much as I view modules as a sorely-needed ECMAScript improvement that addresses some things I've always disliked, this requirement seems likely to Python 3 them as a production feature if the ecosystem isn't ready yet. I think a lot of testing and research is required to ensure that the ecosystem is ready, and probably some aggressive tech evangelism to move things along. Things like brotli and brand new web platform features are more incremental, optional improvements so it is more reasonable to gate them behind things like HTTPS. Modules are such a fundamental quality-of-life improvement - one that we really want everyone to move to as quickly as possible - that gating them seems ill-advised. |
@kg, I don't see why this would require any server configuring for your use cases. Note that CORS headers are only required by sites which wish to expose their modules to third parties, not for your own modules or for local servers or anything like that. |
If I'm testing multiple-domain scenarios locally I'm going to need CORS headers, aren't I? |
Yes, but that's already true for all existing resources (images in canvas, fetch, web fonts, XHR, etc) that aren't grandfathered in under the legacy tech loophole. |
@wanderview - Limiting brotli to HTTPS was (at least partially) based on the fact that deploying it over HTTP would mean existing intermediaries will break it in passing. Not really security related AFAIK. |
Ok, sorry for my confusion. It seemed similar to me, but clearly I'm out of my depth. :-) |
Let's close this. All the arguments have been made and given their fair shake, and in the end the one reason given for relaxing the security properties of the same-origin policy ("adoption hazard" compared with classic scripts) does not suffice to convince the editors. Furthermore, there is the question of implementations. For Chrome's part, we are unwilling to ship modules if doing so introduces another cross-origin security hole. And the prototype implementations in WebKit and Edge conform to the security guarantees in the current spec. That puts us at 3/4 in favor of the current spec. We can perhaps revisit this question with more data later. If it turns out that, after some years in the wild, module adoption is suffering because people are unable to add CORS headers to modules they want to use cross-origin, we can consider relaxing the constraint here, with appropriate security ameliorations (e.g. blocking stack traces and perhaps function.toString). But we cannot regain security after we have given it away. And note that at least one potential large adopter of modules, Facebook, has stated the opposite in preference (toward the current spec) in #1888 (comment). Thanks everyone for the discussion. In the end, after all arguments have been laid out, we need to make a decision, and given the conflicting priorities here of security vs. adoption, I realize not everyone is going to be happy. I hope you can understand why we've made the choice we did. |
Same-origin was buggy and underspecified (and still is), but this is just revisionism: "The web's fundamental security model is the same origin policy. We have several legacy exceptions to that rule from before that security model was in place, with script tags being one of the most egregious and most dangerous." I implemented primitive SOP at Netscape starting in Netscape 2 in 1995. Origins could interact via frames and windows even before <script src=> (e.g., window.open -- lots of bugs in early days). <script src=> did not show up till 1996, in Netscape 3, which had slightly less buggy SOP and an optional alternative (the data tainting model). That we have had bugs to fix up to and including 2015's proxy-as-global-prototype is not a recommendation for mandating CORS for modules, since we'll have to keep supporting non-module scripts, and fixing the never-perfect-now-or-in-the-past SOP, anyway. From this issue I conclude script src=module will not be adopted as well as it would otherwise, and we'll have 3rd party scripts approximately forever. |
A new attack surface is that module scripts parse differently which could lead to details of module scripts (or things that look like them) being observed by an attacker (e.g., through a service worker). So we then have the option of enshrining this new leak, designing workarounds (e.g., bypassing service workers unless CORS is used), or starting with the safe thing. The safe thing is most conservative and would still allow us for lifting that in some way going forward, just like you can typically move from throwing an exception to not doing that. |
@annevk Could you give an example? Yes, modules parse differently. No, this doesn't create any more "attack surface" than today. Modules do not pollute the global scope, so there's less surface to first order. Side channels are all around us and don't weigh one way or the other. Imagine we try to get rid of non-module scripts. We add some CSP directive by which authors can require CORS for script src=. Same would apply to script type=module src=. In the mean time, the use of modules grew better without this CORS security theater, which is a real tax on adoption. To believe security would be worse without CORS, you have to believe it's worse now for all the third party scripts out there, and that we can lead people to adopt both modules and CORS for their future third-party code sharing. Don't get me started on third-party script security (I founded Brave to block the parasitic third party by default). Security is bad enough, we know, but compare apples to apples. The comparison is not between better security by using modules as carrot for the CORS stick. It's rather between today's no-module world and one that we can evolve to that might have more modules and more CORS. It's rare in my experience to see effort in ad-tech to switch to CORS for third party scripts (video is where you see CORS more, via SDKs, due to VAST; it's very rare in display). Don't ask Facebook, they are a giant intranet. Ask Google's publisher and ad partners. Let's see CORS mandated and then talk. So I still say all y'all are doing is entrenching script src= without modules. Hypotheticals about security do not count. But do give a concrete attack that non-CORS type=module introduces, if you can. Be explicit, no hand-waves that apply to non-module scripts as much or more. SWs can see all requests, so the "leak" would apply in either the type=module or old-style script src= case. Nested imports or generated script tags or XHRed code loads or whatever, doesn't matter -- SWs see all. |
E.g., a resource (from which the attacker knows/guesses the URL somehow, but cannot get at the contents (cookies, firewall)) |
@annevk: you wrote "but not with <script>" but you didn't show how the non-module script would emulate the import. If it used XHR, then CORS. But if it used generated script, then "secret-key" in the nested, generated script's URL would leak just the same, apples to apples. Right? |
There is no non-module script in my scenario. Just the resource I outlined. |
@annevk: Your outline is not a parallel construction, it ignores the real-world use of generated and injected scripts in lieu of imports today. This github issue's root comment posits a non-module script alternative, because that's all we have today, and if CORS is required for script type=module, then we will likely continue to have too many such scripts that generate scripts in lieu of imports, ceteris paribus.
This is what I mean by "apples to apples". We don't get to choose the dream-state of high CORS + type=module third party adoption. We only get to optimize script type=module adoption and give developers tools to secure as they see fit. And we can definitely hamper adoption with security theater. Which is exactly the bad outcome that this issue is concerned about avoiding. |
I wish we could explore this together in a more amicable matter. I keep getting the feeling you are trying to tear me down. I think it's important to be on the same page with regards to the attack, irrespective of the tradeoffs involved. So if the only difference with If we can agree on that, then there could be a discussion with regards to likelihood, requiring a MIME type, requiring CORS, etc. As I said to @dherman at some point I personally wouldn't mind leaving CORS out, but requiring a JavaScript MIME type seems important. (Although that wouldn't quite solve remote debugging, unless we somehow agree that leaking more of JavaScript resources across origins than we have done so far is acceptable. Given where Chrome stands that seems unlikely.) |
(Chrome team hat on, as opposed to HTML editor hat which was on for #1888 (comment) and is generally on unless stated explicitly otherwise.) The Chrome team, including the Chrome security team specifically, strongly believes that the presence of insecure constructs on the current web is not a reason for introducing new insecure constructs to the web. Arguments based around such lines of reasoning (i.e. "apples to apples") are not at all convincing to us. |
This is a serious bug report. Could you file a separate issue describing the attack you're alluding to, and the underspecified areas with the SOP? If you're not willing to disclose in a public forum, please reach out to one or more of the editors by email.
Again, if you are using this in the conventional sense of "security theater" (i.e. something like the TSA which does not actually help security), this is either a serious issue or a serious misunderstanding. Can you clarify in what sense you believe CORS's stated security benefits are just "theater"? Maybe it is based on the below misunderstanding?
This is not true. Service workers only seem same-origin resources or CORS-annotated cross-origin resources. Otherwise they get opaque responses which they cannot "see". |
No, because |
Ah, yes. Duh. |
@annevk I'm striving still, and not calling anyone out for "ignorance" either :-/ . That came at me from @domenic -- and IIRC you as well. I'm happy to explore nuanced solutions as time allows, but let's cut the b.s. -- this issue saw @domenic throw "Chrome team" weight around. Complaints about twitter bullying do not move me much in light of a big-bully dominant-share browser vendor ultimatum. If that's how this issue and who knows how many others will be decided, no point in further discussion. When you write "complex set of tradeoffs", I'm with you. So requiring CORS for script type=module needs to consider all of those tradeoffs. That's why this issue was filed. Asking for detailed rationale instead of argument from authority got us mostly nowhere, but your scenario is good and I'm glad you sent it. I still do not see why SW and loader specs can't do same for type=module as they do for other sources of requests. Your "something new we haven't thought of yet" is an argument for defense in depth, also good but too generic to overcome tradeoffs work by itself. CORS gives defense in depth protection against future leaks, but at what price? It seems to me the likely price is continued majority (by any measure) use of old script src= without CORS and without modules. That's a loss. Can we avoid it, minimize the risk? |
@ekr: right, Script cannot produce But push in the other direction on the degree of freedom at hand -- the option to have SW+loader specs keep non-CORS (under the assumption this issue is resolved to remove the CORS mandate for script type=module) cross-origin requests via imports from leaking, just as the SW/HTML spec does for non-CORS cross-origin requests of other kinds. Why would we assume that the import-induced information leak must be permitted in the no-CORS alternative, when we intentionally spec to preclude equivalent-under-transpiling leaks from old-style script src= world? Or if we don't preclude, then what's the new threat? Anyway, it seems fair to raise since the opaque response idea was already used here at an earlier stage of the discussion. |
I don't see where I called you ignorant. I did notice you called me that when I woke up Monday in my Twitter mentions. I'm not sure why you take your beef with Google out on me. And as we're still not on the same page and you're still combative I'm bowing out. Peace. |
@annevk: You're right, you didn't call me ignorant, only my pal @domenic did (but then you schooled him on opaque handles being responses not requests, lol). i still think you misread my tweet, as noted on Twitter, but I'm sorry if it came off as attacking you (which would be silly, since it was you who schooled us on the opaque handle business). I don't care about who forgot what subtle corner case; that's a sideshow and a distraction. (In other news, David was mean to Goliath. That's one version of the story, anyway! ;-) I'm still non-combatively interested in your thoughts on censoring non-CORS cross-origin script type=module import leaks, under the assumption CORS is not required. It could be that this is too much of a hack, I get that -- but it addresses the threat you raised, unless I'm missing something. |
One last thing, then I will shut up. I'm a WHATWG founder. When MS made "the IE team picks" decisions which led to HTML and JS standards effectively or actually shutting down over ten years ago, we didn't just give up, even if we might have agreed on one particular MS decision or another. We founded the WHATWG in 2004 to get a more deliberative and less one-sided process in place to evolve the Web. So even if I agreed that script type=module requiring CORS was the right decision, I'd still want to refrain from consequentialism (the end of safer by default justifies the means of single-vendor trump card throwing). I'd want this issue to get a better and more nuanced response prior to being closed, to weigh that "complex set of tradeoffs" mentioned in #1888 (comment). Just saying "Chrome team picks" doesn't cut it now, any more than invoking the "IE team" fourteen years ago, or the "Firefox team" twelve years ago, to resolve an issue would have settled -- or in fact did settle -- anything. If "the Safari team" differs, this issue will be reopened. It seems to me much better to weigh the tradeoffs first, then close the issue. Ok, I'm done. Thanks for reading this far, if you did. |
In case anyone gets the wrong impression from Brendan's comments, this is not a Chrome-team unilateral decision in any way, but instead a collaborative decision between representatives from all browser engine developers (including some representatives from Mozilla, although non-Gecko parts of Mozilla such as the OP disagree). This can be seen by perusing the upthread comments, but since I know people like to link to downthread comments directly, I thought it'd be important to clarify. Brendan's preferred narrative of Chrome throwing around its marketshare here does not make any sense when confronted with the facts, and analogizing with IE shutting down web standards is not accurate or fair. |
Thanks @BrendanEich. I think my main problem with the alternative (assuming you're not okay with requiring a JavaScript MIME type, as I mentioned earlier) is that I suspect we'll forget about this hole down the road. And the reason I think that is because this is something we would have to add specifically for There is one other resource type that exhibits a similar problem, CSS. And Chrome and Firefox have issues filed to consider making non-CORS-CSS subresources (so subresources of a CSS resource that was loaded without using CORS) bypass service workers, but the CSS leak is a lot more limited since it only applies to So an acceptable alternative in my opinion would be deciding that resources labeled with Now, not having CORS for module scripts does have a lot of downsides apart from not closing the hole, so we need to continue to offer One other problem I can think of is that if CORS is not there by default, would And as I said before, it's easy to remove CORS if we have no traction after a year, it is hard to add it later. |
@domenic It's frustrating to hear you claim that this was a collaborative decision but to have so many of my points consistently go unanswered, or get met with nothing more than "we're not convinced." It feels like the only way I can get attention to this issue is to get some other people involved. :( @annevk I'm having trouble understanding this part:
Can you decode that for me? |
If we decide to work around the hole of import identifiers leaking to service worker by skipping the service worker for submodule identifiers, we need to add conditionals to the module fetching setup to make that happen. Then if we add a Loader API for modules, we again need to make sure that it doesn't leak submodule identifiers. Then for the resource timing API (not "something new we haven't thought of yet" but "something old we haven't thought of yet") we need to make sure it doesn't leak submodule identifiers. And on it goes. Anything we ever do with requests needs to take into account that not all requests are equal from a security perspective and we would need to design specific workarounds for the scenarios those APIs deal with. Hope that helps. |
(Note that in my alternative that I'm not a big fan of, requiring the JavaScript MIME type, we would just treat those requests as any other and leak the identifiers. But then at least the leak is limited to JavaScript and we don't have a fragile system.) |
The Mozilla folks are reviewing this design and are not uniformly convinced that CORS is necessary for a default no-credentials, content-type restricted (which we already wanted) request. Obviously we have not reached internal consensus but didn't want silence to be interpreted as agreement. |
This comment was marked as abuse.
This comment was marked as abuse.
My last comment was marked with disruptive comment, which is fair, and my thread is shown here, but I thought I would leave an update comment anyways. I made a comprehensive reasoning as to why module-scripts should be allowed, #8121, and it basically says what you've said here, but specifically that CORS is unneeded when int I think the biggest benefit of my, and by extension @dherman's suggestion, is that it allows and promotes the use of modular code in all of the contexts JavaScript. Plus, it works on the browser server-side, so it should be easy to parity in a client-side context, such as in the |
@domenic, is this actually ever mentioned that (*Additionally, it seems like people are using JavaScript in the |
For the Mozilla implementation of
<script type=module>
, I have some concerns about the mandatory CORS requirement before we're ready to ship an implementation. I was hoping we could have a discussion here to see if we could get some clarity around the tradeoffs of this particular question.As I see it, the risk of imposing a stricter CORS requirement for module scripts than for classic scripts is an adoption tax: it adds an extra burden for hosts serving modules to multiple origins. The obvious category is CDNs that aren't currently serving CORS headers, but there are also use cases like single-vendor apps with asset subdomains, and perhaps others we aren't thinking of.
While I'm sure some might think of that as a feature -- i.e., using modules to incentivize the adoption of CORS -- we have to contend with the multiplied adoption risks, and it's hard for me to really know how to evaluate that risk. (It'd be helpful to hear from multi-origin authors and hosts, BTW, to hear whether they think this kind of change is something they feel they can absorb, or whether mandatory CORS would cause them any roadblocks.)
Meanwhile, it's not clear to me what the precise rationale is. My assumption is that there's a security argument, but I'd like some clarity about what it is.
The first question is, is this attempting to migrate the web away from existing security issues, or is it attempting to prevent some new security issue? The former seems implausible to me; no matter how widespread the adoption of module scripts, you can't prevent malicious content from using classic scripts. IOW, closing any historical
<script>
gaps in the design of<script type=module>
doesn't stop attackers from using<script>
.If the claim is that modules introduce some new issue, we should try to be clear about what that is. So far I haven't been able to puzzle out a threat model for which module scripts are meaningfully different from classic scripts:
import
but of course classic scripts can already XHR/fetch
.import
/export
syntax is the only difference from the JS grammar, and doesn't plausibly overlap with any data file formats.The primary benefit of requiring no-preflight CORS seems to be the additional power to read cross-origin data (script source, stack traces). But that's not enough to justify the removal of the power to execute cross-origin module scripts without CORS.
If there is a threat model that no-preflight CORS is supposed to protect against with module scripts, I'd like to understand what it is. Even just an example exploit would be helpful. If we can understand the rationale, we can evaluate the tradeoffs.
cc: @domenic, @annevk, @jonco3
The text was updated successfully, but these errors were encountered: