-
Notifications
You must be signed in to change notification settings - Fork 2
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
suggestion: make Object.prototype
exotically reject new properties
#1
Comments
This would certainly break a number of my packages' test suites, but I suspect it would be unlikely to break actual deployed code. |
I guess the assumption here is that people don't add polyfills to My take here is that if we have appetite for backwards breaking opt-in modes that we have some ideas for how to ease the adoption of (see this section), we should try to get the most bang for our buck. @bakkot Can you say more about why the opt-in mode is a lot less palatable? Also cc @salcho for thoughts and data points from his experience trying to mitigate this stuff. |
Yup. We haven't added a new property to
Agreed; my suggestion would only be an improvement over the alternatives in this proposal if we could do it unconditionally instead of needing an opt-in for another route.
New modes which change fundamental semantics are a massive burden for learners, tooling, and libraries which try to be mode-agnostic. Also, I doubt this proposal's proposed modes would see widespread adoption, so like |
Part of the hope is an out-of-band opt-in, perhaps via CSP or something similar, coupled with automatic rewriting by the JS engine's parser that doesn't require any code changes in the common case (that is, in the case where your code doesn't depend on having computed property access to a prototype, and only use As for violating the underlying assumptions in the engines thing: great point and agreed on the risk. I think how we'd implement this is that the prototype properties would always be refactored to be these special symbols or only as internal slots only reachable via new reflection APIs, and whether string keys get magically aliased to them would be implemented on top depends on the mode. It's still messy, but more enumerable than |
I thought this was going to be impractical because of minifiers; I was expecting to find that minifiers would replace x.prototype.f1 = 0;
x.prototype.f2 = 0;
x.prototype.f3 = 0;
x.prototype.f4 = 0; with let _t = 'prototype';
x[_t].f1 = 0;
x[_t].f2 = 0;
x[_t].f3 = 0;
x[_t].f4 = 0; But on actually checking, apparently they don't. (Though I've certainly done that transformation manually, so it's not like it never happens.) So maybe? Still, having to learn that computed property access is not the same as normal property access when the key is specifically "prototype" is going to be a lot of pain. |
Yeah, in general breaking property access symmetry is really terrible, and it is a downside of the automatic rewriting approach. In this case the contention is that the payoff is worth it for moving the needle on this attack vector. And the hope is that people don't really need to learn this in practice. Not saying that nobody out there will hit the asymmetry, but that it'll both be rare and should be cause for pause. Out of the code that internal folks have audited, IIRC getting to the prototype via computed property access is always unintended. |
Keep in mind that it's not just the input code that matters; you've got to look code which is produced by tooling as well. Minifiers apparently aren't relevant, but they're far from the only tools - for example, one of our internal tools would in fact translate |
Doesn't Google Closure Compiler prefer |
This would make
Yes, this is a good point that could weaken automatic refactoring in the browser. FWIW, we did a small study on code that goes through our toolchain (compilation, bundling, minification, etc) looking for breaking transformations and got optimistic results that suggest that these are likely not very common. The handful of occurrences we found were caused by constant folding (
Just a +1 to this point. Our preference would be on landing a mechanism that is transparent to developers and automatic refactoring seems like a good vehicle for that. |
Sorry, I think you've misunderstood the suggestion. I'm not suggesting to make a new kind of freezing available and asking developers to figure out when use it. I'm suggesting we unconditionally change the language so that code can't add new properties to
Looking within a single company isn't very useful, because that means you're only looking at a single toolchain. You'd really need to analyze different toolchains to see the outputs. (And as I mentioned, our toolchain does end up outputting uses of "prototype" and "constructor" as a computed key; more generally it can end up translating any static property access to a computed property access.) |
The toolchain issue seems like a dealbreaker only in the context of non-opt-in. An opt-in is a pretty big hammer; again, if palatable, I imagine toolchains would update to be aware of this asymmetry if the tradeoff is deemed acceptable. |
@zloirock What's that from? |
@bakkot ancient |
@zloirock and what is the value of the Either way though it would only break if people were actually using the property, which is likely to be rare. (Assuming the [[Set]] fails silently rather than throwing.) |
@bakkot it's an object convertible to a randomly generated string for avoiding name collisions - it was before symbols. And I saw a similar approach in some other libraries. |
Ah, sorry for the confusion and thanks for the clarification. In that case I'd only echo @syg comments: this suggestion would have a narrower scope (it protects only the global prototype and not intermediate/application-defined prototypes) and works out of the box for applications that don't use polyfills or add properties to Object.prototype. I think this is a strong solution, both in terms of security and (from experimental data) number of compatible codebases, but I'm afraid it doesn't fully engineer away this class of bugs and is still backwards breaking, so it seems like we should exhaust all options that can give us full security coverage before falling back to Object.prototype only. As a side thought, I wonder if we could reduce the complexity introduced by the new symbols in this proposal by instead making all prototypes be exotic (with a failing/throwing |
Every object is a potential prototype, so I'm not sure how that would work. |
Hey everyone, we've done some work in the last few weeks to gather data on how likely it is that a given toolchain will produce code that is incompatible with secure mode. I've committed a new version of the proposal to reflect that, please take a look again! Kevin's original proposal on this issue continues being feasible if we limit ourselves to solving 'global prototype pollution' (pollution of top level prototypes only), but leaves all intermediate prototypes unprotected. Of all pollution vulnerabilities we've seen at Google, only one relied on polluting intermediate prototypes and the functions responsible for the vulnerability almost always dealt with raw objects (as opposed to application types), which suggests that protecting top-level prototypes only will go a very long way. To call out explicitly what limiting this proposal to top-level prototypes means: we run the risk of protecting an attack technique that is popular today (because it is the shortest path to exploitation), but that might evolve in the future. If that risk materialises, a subset of all vulnerable codebases nowadays will continue to be vulnerable. It would be very valuable to hear feedback on 1) the updated content of the proposal, now with some backing data and 2) where delegates stand in terms of limiting this proposal to top-level prototypes, if the suggested 'secure mode' is not palatable. |
You can't freeze
Object.prototype
without triggering the override mistake. But you could, in principle, makeObject.prototype
an exotic object whose[[Set]]
fails (or, I guess, throws).(Maybe also make
Array.prototype
an exotic whose[[Set]]
fails for valid array indices.)It's possible that would be viable without a new mode, so it's a lot more palatable to me than the ideas here. Would that solve enough of the problem, do you figure?
The text was updated successfully, but these errors were encountered: