Object.prototype.* writable?
I apologize if the question I'm about to ask has already been "dealt with", but I just ran across a nasty instance of it, and wanted to just ping the topic to see what the language guardians have to say about it.
It's a well known fact that overwriting anything in Object.prototype (like Object.prototype.toString, for instance) is a very bad idea, because it breaks for-in looping. It also affects every single object, including even natives/built-ins, often in unexpected ways.
So, a few questions:
Would it be possible to specify that changes to Object.prototype.* do NOT affect any of the natives/built-ins?
Would it be possible for Object.prototype.* to be read-only for ES-Harmony (or even just strict mode)?
By read-only, I mean that changes to it would just silently be discarded. Alternatively (especially for strict mode), warnings/errors could be thrown if attempting to override them?
I think that being able to override something like Object.prototype.toString to "lie" about objects/values is a "security" hole we should consider plugging. For instance, you can "lie" to
document.location.href.toString()
To clarify, you have to overwrite String.prototype.toString
to "lie" in
this particular scenario. But the point is the same.
On Sat, May 7, 2011 at 23:44, Kyle Simpson <getify at gmail.com> wrote:
It's a well known fact that overwriting anything in Object.prototype (like Object.prototype.toString, for instance) is a very bad idea, because it breaks for-in looping.
Properties 'properly' added/updated using Object.defineProperty {enumerable: false} do not break for-in afaik.
- Would it be possible for Object.prototype.* to be read-only for ES-Harmony (or even just strict mode)?
- By read-only, I mean that changes to it would just silently be discarded. Alternatively (especially for strict mode), warnings/errors could be thrown if attempting to override them?
Doesn't Object.freeze(Object.prototype) provide exactly this behavior already?
I think that being able to override something like Object.prototype.toString to "lie" about objects/values is a "security" hole we should consider plugging. For instance, you can "lie" to
document.location.href.toString()
... or a call likeObject.prototype.toString.call(window.opera) == "[object Opera]"
(a common browser inference for Opera) is easily fake'able.
Doesn't this imply the application deliberately 'lies' to itself? Not sure to understand how would this be an issue? It might even be sort of useful for mocking.
,
On May 7, 2011, at 10:24 AM, Cedric Vivier wrote:
- Would it be possible for Object.prototype.* to be read-only for ES-Harmony (or even just strict mode)?
- By read-only, I mean that changes to it would just silently be discarded. Alternatively (especially for strict mode), warnings/errors could be thrown if attempting to override them?
Doesn't Object.freeze(Object.prototype) provide exactly this behavior already?
Unless of course someone has already got code in your page that does Object.freeze = function(){};
Same thing applies to preventExtensions, seal, create, defineProperty, etc and of course the Object constructor itself.
<rant>
It does annoy me that these were not made immutable. The safest way to create an object is still either with an object literal or new SomeFunction;
I'm not convinced that Object.create added anything of value to the language, and certainly wouldn't recommend its use for general object creation. </rant>
It's a well known fact that overwriting anything in Object.prototype (like Object.prototype.toString, for instance) is a very bad idea, because it breaks for-in looping.
Properties 'properly' added/updated using Object.defineProperty {enumerable: false} do not break for-in afaik.
I wasn't aware you could use Object.defineProperty() on Object.prototype
itself. But, see below, because this part of the conversation is really
outside the spirit of what I'm asking anyway. (I'm not talking about if my
responsible code can do it, I'm talking about if other untrusted code does
it first, before my code runs.)
- Would it be possible for Object.prototype.* to be read-only for ES-Harmony (or even just strict mode)?
- By read-only, I mean that changes to it would just silently be discarded. Alternatively (especially for strict mode), warnings/errors could be thrown if attempting to override them?
Doesn't Object.freeze(Object.prototype) provide exactly this behavior already?
It does (I suppose), if you're positive that your code is the first code to run on the page. I'm more talking about code out in the wild, where malicious/hijacked scripts on your page could alter how the page acts before you're more trustworthy code is able to run. Yes, I know that the concept of code security is a whole can o' worms to itself, but I am just implying that this small thing would be helpful in protecting against some of the affects of such behavior.
I think that being able to override something like Object.prototype.toString to "lie" about objects/values is a "security" hole we should consider plugging. For instance, you can "lie" to
document.location.href.toString()
... or a call likeObject.prototype.toString.call(window.opera) == "[object Opera]"
(a common browser inference for Opera) is easily fake'able.Doesn't this imply the application deliberately 'lies' to itself? Not sure to understand how would this be an issue? It might even be sort of useful for mocking.
(see above)
On Sun, May 8, 2011 at 00:35, Kyle Simpson <getify at gmail.com> wrote:
Doesn't Object.freeze(Object.prototype) provide exactly this behavior already?
It does (I suppose), if you're positive that your code is the first code to run on the page. I'm more talking about code out in the wild, where malicious/hijacked scripts on your page could alter how the page acts before you're more trustworthy code is able to run. Yes, I know that the concept of code security is a whole can o' worms to itself, but I am just implying that this small thing would be helpful in protecting against some of the affects of such behavior.
I see. On the other hand when a malicious/hijacked script loads before "trustworthy code", all bets are off anyways.
The malicious script could schedule patching newly loaded code directly without even overwriting Object.prototype (eg. to reuse your example, it could replace document.location.href occurences with a string constant in the 'trustworthy' function source directly).
This means forbidding overwriting properties of Object.prototype would be 'security by obscurity' at best imho.
,
On 11:59 AM, Oliver Hunt wrote:
<rant> It does annoy me that these were not made immutable. The safest way to create an object is still either with an object literal or new SomeFunction;
I'm not convinced that Object.create added anything of value to the language, and certainly wouldn't recommend its use for general object creation. </rant>
I disagree. Object.create is the foundation of a system of delegation. This was the brilliant idea that JavaScript borrows from Self.
The best technique available now for general object creation is Object.create(null), which allows for making objects which do not inherit from Object.prototype. This is a better defense against unintended inheritance than use of Object.prototype.hasOwnProperty, which has multiple hazards.
I agree that the primordials should have been frozen, but as Brendan says, that ship has sunk. But a smart library can now do that job, so a page can elect to have the benefit of frozen primordials if it wishes.
The malicious script could schedule patching newly loaded code directly without even overwriting Object.prototype (eg. to reuse your example, it could replace document.location.href occurences with a string constant in the 'trustworthy' function source directly).
Not if the code in question is inside a self-executing function closure,
which is a pretty common pattern. In that case, the only "vulnerability" to
trusting what you see from Object.prototype.toString
(or
location.href.toString
) is if it's possible that someone paved over them
earlier, either intentionally or accidentally.
This means forbidding overwriting properties of Object.prototype would be 'security by obscurity' at best imho.
I already acknowledged that it's only one tiny piece of the overall "code security" discussion... but it would certainly help a few of those use-cases to be more secure.
Maybe the more appropriate term instead of "secure" is "reliable". For
example, the case of testing for the validity of window.opera
... that
special object could be more trusted/reliable (not faked or accidentally
collided with) if it was impossible to fake the output of
Object.prototype.toString.call(window.opera)
.
On 1:44 PM, Douglas Crockford wrote: I agree that the primordials should have been frozen, but as Brendan says, that ship has sunk. But a smart library can now do that job, so a page can elect to have the benefit of frozen primordials if it wishes.
Again, a "smart library" can only do that if it's guaranteed to be the first code to run on the page. If not (which is usually the case), then all bets are off, unless the language offers some protections.
And "ships being sunk" not withstanding, I think it's a valid question if something like this could be a future candidate for a more "strict" (opt-in) JavaScript, like Harmony/Harmony.Next or "strict mode" itself.
On Sat, May 7, 2011 at 11:54 AM, Kyle Simpson <getify at gmail.com> wrote:
The malicious script could schedule patching newly loaded code
directly without even overwriting Object.prototype (eg. to reuse your example, it could replace document.location.href occurences with a string constant in the 'trustworthy' function source directly).
Not if the code in question is inside a self-executing function closure, which is a pretty common pattern. In that case, the only "vulnerability" to trusting what you see from
Object.prototype.toString
(orlocation.href.toString
) is if it's possible that someone paved over them earlier, either intentionally or accidentally.This means forbidding overwriting properties of Object.prototype would
be 'security by obscurity' at best imho.
I already acknowledged that it's only one tiny piece of the overall "code security" discussion... but it would certainly help a few of those use-cases to be more secure.
Maybe the more appropriate term instead of "secure" is "reliable". For example, the case of testing for the validity of
window.opera
... that special object could be more trusted/reliable (not faked or accidentally collided with) if it was impossible to fake the output ofObject.prototype.toString.call(window.opera)
.On 1:44 PM, Douglas Crockford wrote:
I agree that the primordials should have been frozen, but as Brendan says, that ship has sunk. But a smart library can now do that job, so a page can elect to have the benefit of frozen primordials if it wishes.
Again, a "smart library" can only do that if it's guaranteed to be the first code to run on the page. If not (which is usually the case), then all bets are off, unless the language offers some protections.
All bets are probably still off. The malicious code that's first can load the latter virtuous code as data using cross-origin XHR, or, if the script isn't served with an "Access-Control-Allow-Origin: *", via server-side proxying. Then the malicious code can rewrite the virtuous code as it wishes before evaling it.
I've been at this for a while, as has Crock. I doubt there's any realistic scenario where code loaded later into an already corrupted frame can usefully defend its integrity. If you know of a way to defend against this rewriting attack, please explain it. Thanks.
And "ships being sunk" not withstanding, I think it's a valid question if something like this could be a future candidate for a more "strict" (opt-in) JavaScript, like Harmony/Harmony.Next or "strict mode" itself.
We are currently prototyping such a SES (Secure EcmaScript) proposal, for now as a smart library written in ES5. See < codereview.appspot.com/4249052> for its current in-review state. (Although it already got one LGTM from mzero, I'm waiting on another review before committing.) The security of this library absolutely depends on being run before anything potentially malicious is run in the same frame.
Again, a "smart library" can only do that if it's guaranteed to be the first code to run on the page. If not (which is usually the case), then all bets are off, unless the language offers some protections.
All bets are probably still off. The malicious code that's first can load the latter virtuous code as data using cross-origin XHR, or, if the script isn't served with an "Access-Control-Allow-Origin: *", via server-side proxying. Then the malicious code can rewrite the virtuous code as it wishes before evaling it.
My first reaction to this assertion is to say: "so?" It's a rather moot argument to suggest that code which can be altered before it's run isn't trustable... of course it isn't. The malicious coder in that scenario wouldn't even need to go to the trouble of overwriting Object.prototype.* in the frame, he could just remove the offending if-statement altogether. In fact, he wouldn't even need to modify my code at all, he could just serve his own copy of the .js file. .... ... .......
So what are you suggesting? That regardless of the JS engine, no page's JS functionality is actually reliable, if any of the page's JS resource authors are dumb and don't configure CORS headers correctly, because any malicious script (if it's first on the page) can completely hijack another part of the page? Yup, I agree.
This is a rabbit trail that I'm weary to go down, but I'll just indulge it for one quick moment.... if you are enabling CORS on your server, and not protecting your JavaScript code, you're asking for someone to exploit your code in some XSS type of attack. The whole original purpose of SOP (same origin policy) was to prevent (or cut down significantly) on such things, especially as they relate to being able to trick the browser into sending along cookies/sessions to locations that allow a server to act as man-in-the-middle. If CORS basically completely eliminates any of the protections that SOP gave us, then CORS is a failed system.
But CORS is only failed if you do it wrong. I suspect that's part of the reason CORS is slow to wide-spread adoption (despite plenty of browser support, except Opera), because it's harder to get it right without throwing the barn door wide open. FWIW, I see most implementations of CORS only being on limited URL locations (sub-domains) which are purely web service/REST API's, not general web server roots. That's not to say that noone is doing it wrong, but it is to say, that them doing it wrong is irrelevant to this discussion, because it moots the whole premise.
All this is a moot discussion though, because malicious take-over's of a page are nothing but an exotic edge case, and only enabled if people "do it wrong". The original request stemmed NOT from the malicious hacker scenario, nor from a page "doing it wrong" (per se), but from the "oops, some other piece of dumb code earlier on the page accidentally screwed up and collided with something I need to be inviolate."
I've been at this for a while, as has Crock. I doubt there's any realistic scenario where code loaded later into an already corrupted frame can usefully defend its integrity. If you know of a way to defend against this rewriting attack, please explain it. Thanks.
Off the top of my head, it would seem at first glance that creating a new
iframe for yourself might be the only such way (that is, of course, if you
even are yourself, and haven't been transparently modified or replaced --
see above).
I'm sure both of you are way more experienced at this than me (after my 12 year web dev career so far). But I think you're trying to derail the narrow spirit of my original question by deflecting to much bigger questions. The appropriate forum for that type of discussion was when CORS was being conceived and brought about. As people love to say on this list: "that ship has sailed".
None of this exotic "what-if" scenario indulgence invalidates my original request, that a clearly known bad-practice (changing some, not all, particular behaviors of natives) leads to code that is less than reliable, and can we make it a little less so by having the engine protect certain key pieces.
And btw, contrary to some people on this list who seem to operate almost exclusively on theoretical principle, "security through deterrence" (not the same as "obscurity") is a long-established and perfectly valid approach. No computer system (SSL included) is completely immune to attack... we live with somewhat less than ideal theoretical utopia because we construct systems which are "pretty good" at deterrence, and with that we sleep peacefully at night.
What I'm suggesting should be viewed as another peg in the system of deterrence, and nothing more.
On Sat, May 7, 2011 at 1:17 PM, Kyle Simpson <getify at gmail.com> wrote:
Again, a "smart library" can only do that if it's guaranteed to be the
first code to run on the page. If not (which is usually the case), then all bets are off, unless the language offers some protections.
All bets are probably still off. The malicious code that's first can load the latter virtuous code as data using cross-origin XHR, or, if the script isn't served with an "Access-Control-Allow-Origin: *", via server-side proxying. Then the malicious code can rewrite the virtuous code as it wishes before evaling it.
My first reaction to this assertion is to say: "so?" It's a rather moot argument to suggest that code which can be altered before it's run isn't trustable... of course it isn't. [...] because any malicious script (if it's first on the page) can completely hijack another part of the page? Yup, I agree.
That was my point. Since you agree, I don't understand your point:
Again, a "smart library" can only do that if it's guaranteed to be the first
code to run on the page. If not (which is usually the case), then all bets are off, unless the language offers some protections.
What protections do you have in mind? And how, were your protections to be adopted, would all bets no longer be off when loading a virtuous library into a frame in which malicious code had already run first?
[...] If CORS basically completely eliminates any of the protections that SOP gave us, then CORS is a failed system.
CORS is not the issue here. In the absence of any Access-Control-Allow-Origin headers, or if we hypothetically remove CORS from the picture, then the unsolvability remains because of server side proxying.
All this is a moot discussion though, because malicious take-over's of a page are nothing but an exotic edge case, and only enabled if people "do it wrong". The original request stemmed NOT from the malicious hacker scenario, nor from a page "doing it wrong" (per se), but from the "oops, some other piece of dumb code earlier on the page accidentally screwed up and collided with something I need to be inviolate."
Some people's edge cases are other people's central focus. Your message was a response to Crock's claim, which clearly issues of malice, not merely accident, as central.
As for accident, it depends how dumb that earlier piece of code was. If it modifies primordial methods so that they no longer satisfy their contracts in way that later code is ignorant of and unprepared for, then again, it's hard to do any practical and correct JS coding under platform assumptions this weak. See the thread starting at < esdiscuss/2011-April/013654>.
OTOH, if these earlier dumb scripts are not that dumb, then the later smart library should still be able to succeed. As a concrete test case, we should ask what kind of primordial-contract-preserving damage an initial dumb-but-not-malicious early script might do that would invalidate the protections the SES library provides. I think this should be an interesting question.
Le 07/05/2011 20:44, Douglas Crockford a écrit :
On 11:59 AM, Oliver Hunt wrote:
<rant> It does annoy me that these were not made immutable. The safest way to create an object is still either with an object literal or new SomeFunction;
I'm not convinced that Object.create added anything of value to the language, and certainly wouldn't recommend its use for general object creation. </rant> I disagree. Object.create is the foundation of a system of delegation. This was the brilliant idea that JavaScript borrows from Self.
I agree. Object.create is what should have been present in the first place. Even before functions as constructors. Even before putting Object.prototype as the default prototype object for the object literal (in ES5, calls "new Object()" which sets the [[Prototype]] to the standard built-in Object prototype object. See 11.1.5 then 15.2.2.1 step 4). One strawman even suggests an extension to object literals in order to declare the prototype object [1] Object.create is the "easiest" abstraction based on which, other constructs can be created. It gets back to the basic definition of what an object is (ES5 4.3.3): "An object is a collection of properties and has a single prototype object. The prototype may be the null value." Before Object.create, there was no easy way built-in in the language to create an object following this definition. You had to create the abstraction yourself. With Object.create, the abstraction is natively here (properties are also considered with the second optional argument).
David
My first reaction to this assertion is to say: "so?" It's a rather moot argument to suggest that code which can be altered before it's run isn't trustable... of course it isn't. [...] because any malicious script (if it's first on the page) can completely hijack another part of the page? Yup, I agree.
That was my point. Since you agree, I don't understand your point:
My point was that your argument was deflecting from the premise of the original request in a way that makes the conversation impossible to proceed. If you re-define the premise/assumptions to an unreasonable level, you make logical reasoning impossible. For instance, arguing that a virus could infect the JS engine of the browser is a possible scenario, but not a useful one to discuss in this context, because it fundamentally violates the premise. Similarly, arguing that a network router could be hacked to change code in-transit is conceivable, but moot and pointless to the spirit of my question.
The premise of the question is, whatever the state of the code on the page is (or how it got there), is there a way to prevent some piece of code from intentionally or unintentionally altering some important behaviors of native objects such that subsequent code could be tricked or tripped up? Furthermore, the spirit of the question was, granted there is no 100% system for that, but can we move the ball a little closer to the goal line with a very narrow idea of restricting a few things, in opt-in modes of JavaScript?
What protections do you have in mind?
Specifically, I think:
-
All changes to Object.prototype.* should be ignored/error'd, such that the predefined members of Object.prototype.* (the native built-ins) are immutable. This would not necessarily prevent extensions to Object.prototype, simply prevent redefinitions of existing native functionality.
-
OR; Let Object.prototype.* members continue to be mutable, but let those effects ONLY affect user objects, and not built-in native objects (like String, Array, etc). However, a few of Object.prototype.*'s members should still be considered for immutability, like for instance
toString()
, which is commonly used in the scenario of borrowing its functionality via.apply()
, as described earlier withwindow.opera
. -
OR; There are other more exotic ideas I could advance, such as keeping Object.prototype.* mutable, but then exposing an additional interface like Object.prototype (which is the original prototype, and is immutable).
And how, were your protections to be adopted
I was specifically suggesting they be part of one of the future opt-in
modes, either for Harmony/Next, or more appropriately even, perhaps a future
"strict mode"... or even call it "safe mode". "use safe";
has a nice ring
to it.
would all bets no longer be off when loading a virtuous library into a frame in which malicious code had already run first?
in the narrow context of what I'm suggesting, the protections afforded by the JavaScript engine would limit the ability of prior-malicious (or stupid) code from influencing some of the natives which my code might need to rely on.
As for accident, it depends how dumb that earlier piece of code was. If it modifies primordial methods so that they no longer satisfy their contracts in way that later code is ignorant of and unprepared for
Not all modifications are easily feature-testable such that later code would have any hope of preventing its own ignorance.
OTOH, if these earlier dumb scripts are not that dumb, then the later smart library should still be able to succeed.
To me, the notion of a "smart library" (even for the purposes of safety) is
rather absurd. A fundamental assumption here is that there are in fact a
certain subset of things which no JavaScript code should be allowed to do,
so as to "play nicely" with other code. Extending Object.prototype has
almost universally been taken as one such thing. Circumventing
location.href
inspection would be another good candidate.
We can't then say "well, we'll make an exception for this one smart library, and let him much with that stuff"... because then the "smart library" becomes the single-point-of-failure(aka attack).
That's why I approached this narrow question as specifically opt-in protections from the engine (non-circumventable), rather than in the user-space.
whoah! I didn't know that. Why doesn't that work with:
function o () {}; o.prototype=null; new o();
?
As for the current discussion, I should think that some way to instantiate a new "clean" global object, and inject it into the current scope would be somewhat more useful, as it would enable newly written scripts to depend on the assumption that those global constructors are a particular shape, and leave old scripts which depend on the mutable behavior alone. It would even enable new scripts to depend on mutable "global" constructors- Their own mutated copies that is. I vaguely remember some proposal along these lines. And some libraries make their own copies of the globals to mutate.
In fact, isn't this the whole point of modules, and module scope? If you evaluate a script inside a module scope, your global prototypes are protected anyway. This is already kind of where JS is heading. I saw your (Kyle) tweets last night about "module formats", and I sort of agree, but we also have to be pragmatic. Browsers are the way they are, it's just reality, and as a result there's a limited domain of paths forward when it comes to extending the javascript language, the scripting environment, with the goals of scalability and modularity in mind. TC39 thinks very hard about these things, and I'm excited about ES.next modules, and the way node.js modules work (sort of)
On Sat, May 7, 2011 at 5:05 PM, Kyle Simpson <getify at gmail.com> wrote:
My first reaction to this assertion is to say: "so?" It's a rather moot
argument to suggest that code which can be altered before it's run isn't trustable... of course it isn't. [...] because any malicious script (if it's first on the page) can completely hijack another part of the page? Yup, I agree.
That was my point. Since you agree, I don't understand your point:
My point was that your argument was deflecting from the premise of the original request in a way that makes the conversation impossible to proceed. If you re-define the premise/assumptions to an unreasonable level, you make logical reasoning impossible.
Good, we're making progress. Previously I was not responding to your original request, I was responding to your response to Crock's message. Hence our confusion about premises. Thanks for making your's clearer. As for your original request, now that I better understand what you're looking for, I think SES is again a good answer. Should SES become a directly supported standard, there would be some opt-in as you suggest. For now, for SES as implemented on ES5 codereview.appspot.com/4249052, the interim
opt-in is to run initSES.js first in the JavaScript context in question. In the case of a browser frame, the interim opt-in is to place
<script src="initSES.js></script>
before any other scripts.
Whereas the strict mode and anticipated harmony opt-ins are per-script within a context, the SES opt-in needs to be per-context (i.e., per frame). Here, I'll avoid speculating about the concrete form of a standard SES opt-in and answer your original request in terms of this interim SES-as-ES5 library opt-in:
On Sat, May 7, 2011 at 9:44 AM, Kyle Simpson <getify at gmail.com> wrote:
I apologize if the question I'm about to ask has already been "dealt with", but I just ran across a nasty instance of it, and wanted to just ping the topic to see what the language guardians have to say about it.
It's a well known fact that overwriting anything in Object.prototype (like Object.prototype.toString, for instance) is a very bad idea, because it breaks for-in looping. It also affects every single object, including even natives/built-ins, often in unexpected ways.
So, a few questions:
Would it be possible to specify that changes to Object.prototype.* do NOT affect any of the natives/built-ins?
Would it be possible for Object.prototype.* to be read-only for ES-Harmony (or even just strict mode)?
By placing
<script src="initSES.js></script>
at the beginning of your frame, earlier than loading any other scripts, you opt-in to SES and make Object.prototype.* read-only.
- By read-only, I mean that changes to it would just silently be discarded. Alternatively (especially for strict mode), warnings/errors could be thrown if attempting to override them?
Exactly. Strict assignments to these properties will fail with a thrown exception. Non-strict assignments will fail silently. But neither can mutate these properties.
I think that being able to override something like Object.prototype.toString to "lie" about objects/values is a "security" hole we should consider plugging. For instance, you can "lie" to
document.location.href.toString()
... or a call likeObject.prototype.toString.call(window.opera) == "[object Opera]"
(a common browser inference for Opera) is easily fake'able.
SES does not freeze objects or properties outside the ES5 spec. So it does nothing to freeze document's "location", location's "href", or window's "opera". If you want to prevent these from being modified by post-opt-in scripts, you can extend your initialization-time "opt-in" to opt-in to SES and whatever additional freezing you'd like.
But again I may be answer something other than what you are really asking. Given that document.location.href and window.opera return what you expect, SES guarantees the rest of your expressions --- <string>.toString() and
Object.prototype.toString.call(*) -- do what you expect. Is that what you were really asking for here?
Note that even this "rest of your expressions" require more that just Object.prototype.* to remain reliable. The first also requires String.prototype.toString. The latter also requires Function.prototype.call. SES guarantees that all these and more remain reliable.
Good, we're making progress. Previously I was not responding to your original request, I was responding to your response to Crock's message. Hence our confusion about premises. Thanks for making your's clearer. As for your original request, now that I better understand what you're looking for, I think SES is again a good answer. Should SES become a directly supported standard, there would be some opt-in as you suggest. For now, for SES as implemented on ES5 codereview.appspot.com/4249052, the interim opt-in is to run initSES.js first in the JavaScript context in question. In the case of a browser frame, the interim opt-in is to place
<script src="initSES.js></script>
Unfortunately, we're back to the chicken-and-the-egg... if I could guarantee that my code was the first to ever run on any page, almost none of the problems I'm complaining about would be an issue, because I could just make sandboxed copies of what I needed, and store them privately inside a closure. Being able to "run-first" is the key component that isn't true, and if it were true (which is required of "initSES.js"), then I wouldn't need "initSES.js".
So, while I agree that the direction of SES seems to be along the lines I am asking for, the interim story doesn't really hold much water. The good news is that some movement is happening toward it. I hope that continues.
Is the thought that you would have a similar opt-in to "SES" as you do "strict mode", eventually? Or something else?
On Sat, May 7, 2011 at 8:04 PM, Kyle Simpson <getify at gmail.com> wrote:
Good, we're making progress. Previously I was not responding to your
original request, I was responding to your response to Crock's message. Hence our confusion about premises. Thanks for making your's clearer. As for your original request, now that I better understand what you're looking for, I think SES is again a good answer. Should SES become a directly supported standard, there would be some opt-in as you suggest. For now, for SES as implemented on ES5 codereview.appspot.com/4249052, the interim opt-in is to run initSES.js first in the JavaScript context in question. In the case of a browser frame, the interim opt-in is to place
<script src="initSES.js></script>
Unfortunately, we're back to the chicken-and-the-egg... if I could guarantee that my code was the first to ever run on any page, almost none of the problems I'm complaining about would be an issue, because I could just make sandboxed copies of what I needed, and store them privately inside a closure. Being able to "run-first" is the key component that isn't true, and if it were true (which is required of "initSES.js"), then I wouldn't need "initSES.js".
So, while I agree that the direction of SES seems to be along the lines I am asking for, the interim story doesn't really hold much water. The good news is that some movement is happening toward it. I hope that continues.
Is the thought that you would have a similar opt-in to "SES" as you do "strict mode", eventually? Or something else?
Similar. But it does need to be different because strict mode is per script within a context, whereas SES needs to be per context. That's why, within a page, the opt-in needs to be page-wide, and the opt-in needs to be noticed by the browser before it starts executing scripts on that page.
the Loader.createBase() and Loader.create(base, resolver) APIs enable the creation of a new context and a new module loader which will load modules into that context, respectively. This allows opt-in to SES post frame creation, by creating a new context and loading the SES code into that context.
What I here call "context" that strawman calls "base library", and corresponds to the unique set of primordial non-host objects currently created by creating a new browser frame.
I'm generally on board that modules are the real solution to ensuring reliable access to built-ins. It has occurred to me that a reliable, ES5 level, way to access built-ins might be a useful feature for implementations to do something like the following:
For example, there might be a "frozen" global named something like "ES5Builtins" whose values was a preinitialized object structures of the form:
Object.freeze( { Object: Object.freeze({ getProtypeOf: Object.getPrototypeOf, getOwnPropertyDescriptor: Object.getOwnPropertyDescriptor, create: Object.create, ... keys: Object.keys, Object_prototype: Object.prototype, prototype: Object.freeze({ constructor: Object, toString: {}.toString, toLocaleString: {}.toLocaleString, valueOf: {}.valueOf, isProtototypeOf: {}.isPrototypeOf, propertyIsEnumerable: {}.propertyIsEnumerable }), }), Function: Object.freeze({ Function_prototype: Function.prototype, prototype: Object.freeze({ constructor: Function, toString: Function.prototype.toString, apply: Function.prototype.apply, call: Function.prototype.call, bind: Function.prototype.bind }), }), Array: Object.freeze({ isArray: Array.isArray, Array_prototype: Array.prototype, prototype: Object.freeze({ constructor: Array, toString: [].toString, ... }), }), ... })
From: Dean Landolt Sent: Sunday, May 08, 2011 10:17 AM
Unfortunately, we're back to the chicken-and-the-egg... if I could guarantee that my code was the first to ever run on any page, almost none of the problems I'm complaining about would be an issue, because I could just make sandboxed copies of what I needed, and store them privately inside a closure. Being able to "run-first" is the key component that isn't true, and if it were true (which is required of "initSES.js"), then I wouldn't need "initSES.js".
Forgive me if this has come up already and I missed it but wouldn't it be enough if there were some mechanism to validate the integrity of Object.prototype by asking the host env for a fresh copy and comparing identities? Even if the frozen ship has sunk ISTM it ought to be enough to be able to reliably detect the hijacking. This would probably be best left to a web platform standards body but wouldn't that be a good place to inject that kind of unforgeable factory for Object.prototype?
I would definitely support or appreciate a mechanism by which a clean/fresh copy of Object.prototype could be arrived at, without the hackiness of either launching an iframe or something like that. That's what my Object.prototype was kind of getting at, a few messages ago.
I don't think it's enough to just detect that it's bad, if there's no way to undo the badness and get at the native functionality. But giving us another parallel interface which IS read-only would be, in my mind, a pretty simple solution to this problem. Of course, this would need to be true not just for Object but all the natives, like String, as well.
I'd be in favor of this as a shorter term solution than SES.
[+ataly] the author of the paper cited below.
On Sun, May 8, 2011 at 6:35 PM, Kyle Simpson <getify at gmail.com> wrote:
I'd be in favor of this as a shorter term solution than SES.
Shorter term? SES can shortly be used on any ES5 platform (once < codereview.appspot.com/4249052> is reviewed and committed). SES's
robustness properties are already being formally investigated < www-cs-students.stanford.edu/~ataly/Papers/sp11.pdf> and look both
simple and powerful.
For the formally inclined, I strongly recommend this paper. For the rest, SES is just ES5 with frozen primordials and an 'eval' function and 'Function' constructor enforcing strict mode and lexical scope -- no ambient access to the global object or non-whitelisted global variables.
I apologize if the question I'm about to ask has already been "dealt with", but I just ran across a nasty instance of it, and wanted to just ping the topic to see what the language guardians have to say about it.
It's a well known fact that overwriting anything in Object.prototype (like Object.prototype.toString, for instance) is a very bad idea, because it breaks for-in looping. It also affects every single object, including even natives/built-ins, often in unexpected ways.
So, a few questions:
Would it be possible to specify that changes to Object.prototype.* do NOT affect any of the natives/built-ins?
Would it be possible for Object.prototype.* to be read-only for ES-Harmony (or even just strict mode)?
By read-only, I mean that changes to it would just silently be discarded. Alternatively (especially for strict mode), warnings/errors could be thrown if attempting to override them?
I think that being able to override something like Object.prototype.toString to "lie" about objects/values is a "security" hole we should consider plugging. For instance, you can "lie" to
document.location.href.toString()
... or a call likeObject.prototype.toString.call(window.opera) == "[object Opera]"
(a common browser inference for Opera) is easily fake'able.