zenparsing / proposal-private-symbols

A proposal for private symbols in JavaScript
21 stars 4 forks source link

Leaking private symbols #8

Open caridy opened 6 years ago

caridy commented 6 years ago

One of the big issues that we have identified when working with private fields, and the potential generalization of private fields is the leakage of them. Issue #2 and #7 are examples of such leakage, other examples are:

  1. the possibility to introspect any private state anywhere in the program by just replacing Symbol.private to collect all new private symbols.
  2. invoking a method that uses private symbol in any way, e.g.: (class foo { disable() { this[privateSymbol] = false; } }).

As part of the generalization of private fields we found two mitigation strategies to address both of these issues, and I think this proposal can use them:

  1. use syntax instead of an API to create new private symbols.
  2. use grammar to install new private symbols.

Number one is fairly simple, something like private #foo or something similar could be enough, while number two is more complicated. @erights will probably argue that in fact we need grammar for installation, initialization and access. With those mechanism in place, branding is possible, and leaking is impossible.

In general, even though I haven't read thru the whole thing, this as a lot of similarities with the pseudo proposal for the generalization the private fields.

littledan commented 6 years ago

Seems like, by the time all these changes are made, the result wouldn't be much different from the current class fields proposal (modulo private name declarations outside classes, which have already been under discussion in the context of the other proposal).

bathos commented 6 years ago

@littledan I think that’s true. But from my perspective, “private name declarations outside classes” is the key thing. What continues to make me nervous about the current proposal is mainly that “outside classes” seemed to be left as a problem-to-solve-later. In other words, it’s hard to get on board with a particular higher-level abstraction when the building-blocks form is left as an open question, especially when you anticipate needing the latter more often than the former, and it seems contrary to minmax principles to not introduce the lower level tool first (or at least concurrently).

zenparsing commented 6 years ago

To restate for my own benefit:

For these reasons (according to the argument), it is better to provide a syntax-only solution, where private "key" creation and private state installation on objects is only allowed via syntax.

I think these are fair points and will be responding to them soon. Thanks!

bathos commented 6 years ago

@zenparsing regarding bullet 2, I see that as a positive facet of this proposal, since being able to do something inadvertently is an unavoidable consequence of being able to do something intentionally. Is there any feature which cannot be used badly?

jridgewell commented 6 years ago

regarding bullet 2, I see that as a positive facet of this proposal

I agree, but not for the same reason. I think this is a leak in branding, not a leak in encapsulation. It's impossible for foreign object to access its new private field unless is uses the foo.p.disable method (or any other methods with the privateSymbol in scope) so it seems perfectly encapsulated. But I can no longer guarantee that the foreign object isn't an instance of foo which is just branding (and better left to WeakSets according to this proposal.

thysultan commented 6 years ago

In this proposal Symbol.private can be hijacked to observe private symbol creation.

This point keeps coming up, but i think any syntax you could come up with to mitigate this will never truely archive this lofty goal.

It almost feels like a false sense of privacy. For as long as the author can control when the script is loaded they can hijack private state, syntax or otherwise. And if they can't then it follows that they can't do the same with Symbol.private.

As an example example with hash#private syntax.

Exhibit A: I could just import the script as text and replace all occurrences removing the declarative #private syntax, and load/import the script inline as js to the pleasure of my nefarious needs. In a node environment this is made even easier.

caridy commented 6 years ago

@thysultan to do so, you must either be in control of the execution of the program (you're the puppets' master), or your code executes before the other code so you could modify the environment to take control of the execution of the code-to-be-executed, so you can parse and evaluate the new code at will. This is not that simple, and in most cases, imposible due to other security layers (e.g.: CSP in browsers).

On the other hand, Symbol.private being an API, it means that any code that has access to that API could replace it, so any newly created symbol is now observable. Our frozen realm proposal is supposed to protect again this particular case by freezing all those APIs to prevent taming, but we have a long road ahead before we can get that into browsers.

Nevertheless, these are the kind of things that some of us, at the TC39 committee, care about, and try to be coherent with other proposals.

thysultan commented 6 years ago

or your code executes before the other code so you could modify the environment to take control of the execution of the code-to-be-executed, so you can parse and evaluate the new code at will. This is not that simple, and in most cases, imposible due to other security layers (e.g.: CSP in browsers).

@caridy Not necessarily. Exhibit B: Being the last script to load in a browser context – you could for example scrap all scripts in document.scripts extract the contents of the scripts you want, and create new script elements with the modifier content or dynamically import inline encoded urls. That is to say i'm not aware of any hard blockers in CSP that could prevent this.

If a desired recipient is keen on accessing a private field, first-class syntax support won't prevent them from achieving this goal.