4 ms·
Author here. Thankyou so much for this summary of java's approach. I learned Java when I was a kid in the 90s and I remember seeing some SecurityManager stuff i
by josephg 5y ago
Author here. Thankyou so much for this summary of java's approach. I learned Java when I was a kid in the 90s and I remember seeing some SecurityManager stuff in the java standard library and I remember having no idea what that was or why I would want any of it. Its funny to think that decades later I would propose re-inventing it.
As for the code, surely something like this could work?
var lines = readFile("library-data.txt", capabilityToken);
But yeah, even in the example in my post the capability tokens are annoying and feel cumbersome.
Another poster in this thread suggested maybe expressing capabilities in your package.json file. Maybe when you pull in a dependency you can say "oh, and rather than inheriting all my capabilities, only give this library access to capability X. That would provide a nice ramp, but there's a whole new set of problems that way, since you'd need to be able to express something like "the capability you need for the redis client is network access to this specific IP address". And that specific capability needs to be passed all the way through the dependency tree to whatever finally opens the socket.
Expressing this in a granular way in code is easy, but noisy. But if we do it in package.json, maybe thats not going to be expressive enough.
Anyway, like you, I hope someone smart takes the time to have another stab at this. The security model where we trust all software engineers is obviously breaking down at this point. Short of a model like this, I'm not sure how we can really solve this problem at all. In any case, thankyou for sharing your wisdom.
- mike_hearn 5y agoRe: your example. What is "capabilityToken" in this case? What does it grant you, precisely? Is it a directory? A file? Something else? The classical approach to using caps with files is you create a File type of some kind, which encapsulates the permissions and lets you derive from it e.g. sub-directories, files in that directory but not navigate up the tree. Or it's associated with some whitelist of files. For that to work you need not only a carefully designed set of types but also they must be able to protect their internals. JavaScript historically hasn't had this, I don't know about modern versions, but the ability to restrict monkey-patching, reflection over private fields etc is a must.
- josephg 5y ago> For that to work you need not only a carefully designed set of types but also they must be able to protect their internals. JavaScript historically hasn't had this, I don't know about modern versions, but the ability to restrict monkey-patching, reflection over private fields etc is a must. At the bottom of the post I sketched out how we could make this work in practice in javascript. We can use a Symbol[1], and then have that be a key into a Map owned by the builtin capabilities library. That would make the token itself safe from being messed with. But so long as the capabilities library uses whatever the object is as a key in a JS Map (with the value being the token's scope), we could just as easily use anonymous objects or something else. [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- mike_hearn 5y agoI think the issue is more the code that uses the capability itself. Like, if I can just read the capability straight out of the object that owns it, or monkey-patch the definition of some other object it calls into so I can use its capabilities indirectly, then you still lose. That's what I meant by playing defense all the time. If you give a bit of sandboxed code a generic utility object, it can all go wrong.
- josephg 5y agoThe idea here is that there's 2 things: The token (a Symbol() or something) and the scope of capabilities which that token gives you. The capabilities themselves are stored in a Map that you don't control. Javascript function scopes give us everything we need to hide that map and make sure nobody can modify it. The only methods which are exposed are things like getScopeForToken() which reads from the map (and does a deep clone) then returns that scope object. In privileged methods like fs.writeFile(), you don't pass the scope. You pass the token. And that method would explicitly go and check if that token has the scope that it needs to write to the passed path. But I do hear you about playing defense. I mentioned it in the post - there's probably a bunch of subtle ways you could use javascript to mess with things. Covering all of these cases would need some serious rigor.