5 ms·
It boggles my mind that languages and package managers do not support ACLs for libraries. leftpad has no need of install scripts, nor `eval`, reflection, or ac
by Groxx 5y ago
It boggles my mind that languages and package managers do not support ACLs for libraries.
leftpad has no need of install scripts, nor `eval`, reflection, or access to my disk or the network. nor should it be allowed to gain them in the future, at least without a million alarm bells ringing and explicit approval.
ACLs would allow establishing "moats" of dramatically-more-difficult-to-attack libraries, and encourage libraries to voluntarily reduce their attackable surface to make them more likely to be approved. Instead we have this.
- pxc 5y agoIs the way Deno security works kinda what you have in mind? Some Linux distros also ship SELinux or AppArmor policies with packages by default, but that seems like a kind of inside-out proposition from what you're describing.
- Groxx 5y agoSELinux does a heck of a lot to improve this on a per-binary / per-user basis, yeah. But I want this in process. Because libraries are in practice very frequently treated as black boxes (like binaries)[1], but without any ability to limit their access like we can for users/binaries/etc. There are some WASM things that do kinda what I want and pull off neat end results, but they're kinda tough to use together, and have obvious perf/debuggability/etc costs. e.g.: https://github.com/dtolnay/watt https://github.com/dtolnay/watt [1] unfortunate in many ways, but largely reasonable IMO (simpler abstractions of things we don't want to think about is the whole point of shared libraries), and I don't believe the industry is capable of reversing course on this either way.
- pxc 5y ago> (simpler abstractions of things we don't want to think about is the whole point of shared libraries), and I don't believe the industry is capable of reversing course on this Far from reversing course, the industry seems to be interested in accelerating on the current one— containerization as a deployment mechanism is about doubling down on the black box-ness of binaries. Paradoxically, this doubling down on ‘the whole point’ of shared libraries is also a destructive innovation with respect to the advantages of shared libraries, because it also means surrendering the fine-grained resource sharing that shared libraries, as packaged traditionally, allow.
- deleted 5y ago[deleted]
- frutiger 5y agoThis simplistic model breaks as soon as a more privileged package passes a callback into a less privileged callback without fully constraining what that callback can do. Which, I suspect, is impossible to achieve in practice.
- Groxx 5y agoPerfection is unachievable, yeah. But "privileged package passes values to less privileged package and may be exploited" is so much smaller of a surface than "anything anywhere can os.Exec(whatever it damn well pleases)". Security is not binary, nor can it be. Air-gapping isn't even enough, as is repeatedly demonstrated. That's not a reason to be yolo.
- danenania 5y agoPerhaps it could be achieved with static analysis? 1. Each package declares what permissions it needs in package.json (if any). 2. I declare in my project's top-level package.json what should be allowed. 3. npm install fails if any dependency's permissions exceed what I've defined in 2, and points me to the offending package(s). Then at runtime just my project's restrictions can be applied across the board.
- Groxx 5y agoYeah, even something this simple would likely be enough to prevent several of these supply-chain attacks - they tend to have to add unsafe abilities to historically-safe (but widely used, in part due to their safety) libraries. And this would be fairly simple to add onto existing languages/libraries... which is part of why I'm stunned it hasn't happened yet in practice, even on frequently-exploited systems like NPM. The same kind of permissions system would also help warn you when you're importing a "does far more than you want" sub-tree. If `pad` imports `leftpad` and tomorrow `leftpad` imports the entire Kubernetes codebase, you might actually notice it and fix that instead of your builds just getting a bit bigger and hoping a reviewer notices the lockfile diff (which is probably hidden by default!). Again, not perfect, but better... and applies pressure to do better, possibly improving the ecosystem as a whole. Future languages/tools/etc could be dramatically more intelligent about these kinds of things, but even the crappy basics don't exist in the extreme majority of cases. You could build it yourself many times, but almost nobody does that. Default behaviors matter.
- chriswarbo 5y agoThis seems more suitable to a capability-based security model, where dangerous features like eval could be kept in separate packages, and only those packages which depend on them (non-transitively) are able to import and use that functionality. The reason to disallow importing transitive dependencies is that we can write packages which use that functionality, but only expose a safer/restricted form; e.g. we might forbid dependencies on `eval`, but allow dependencies on a `mock` library, even if `mock` happens to depend on `eval`. A sibling points out that restrictions can be broken when different packages pass callbacks to each other. Thankfully, that's exactly what capabilities are for (code is written without any "ambient authority", and instead requires capabilities to be passed in as arguments; e.g. see the Confused Deputy Problem)
- Groxx 5y agoAgreed, capabilities seem like a fundamentally better approach in basically all ways that matter. I should probably clarify that I mean "ACL" in the sense of "some kind of control, I hardly care what" since it's an easily-recognized term at least. And there probably will still be an ACL somewhere, e.g. to allow the library to use that eval at all, and then we trust it to pass capabilities correctly to others (or perhaps don't trust it! maybe I don't use that, and don't want it to silently appear later!).
- cormacrelf 5y agoIn general, as the sibling points out, runtimes can't tell which dependency a particular API call is coming from if it's all bundled into one blob. Unfortunately most APIs, for example in WASI for ease of writing POSIX code, don't have an argument for a capability assertion to be made. So you can only realistically associate one set of capabilities to a single VM unit. So what you actually need to do is split your program into more VM units, not unlike code splitting in bundler-integrated JavaScript HTML5 routers. You can build that atop a WASM runtime, if you pass the ability to call other WASM modules into other WASM modules. And then you could pass literal capability tokens around. You just need to split your code up. Then you can bridge them back again with trusted code, and you can add additional constraints on API calls between modules (e.g. callbacks don't work across units, or if they do, then they remember the capabilities they were instantiated with, something like that). If there were an easier way to have your dependency management system do this code splitting for you, then it would be feasible.
- zaphirplane 5y agoI disagree with how this conflates layers and spheres of responsibility The package manager fetches dependencies recursively into your chosen directory running as your choose account. The runtime executes with your chosen account with whatever permissions have been granted. Now having the runtime track whether the caller of a library is another library or your application. Or for the runtime to allow reject runtime api seems better left to the os to control with container, sandbox or transitional ACL
- andylynch 5y agoThe is roughly what you are to be able to do with the Java SecurityManager- it was especially intended for code mobility scenarios where you can’t be certain that everything is well-behaved (and not just applets- we used it server side). But wrangling all those capabilities and policies gets really really difficult on a decent sized system, and comes with a runtime overhead to boot.
- Groxx 5y agoFairly similar at least, yeah. Java has enough power in its reflection APIs, and widely-enough used reflection, that this kind of thing is practically required to be part of the runtime + have a cost. And that's probably the safest way to do things anyway. I do think though that as it seems to be only a runtime thing, it's probably warning you far too late. If it were baked in statically so, when you pulled in a new http lib (or just updated it), you approved the popup that said "let X access network and files?", it'd probably be a lot less painful 95% of the time. (since sometimes you'll want it more fine-grained) --- Since I can't glean it from what I've skimmed so far: does SecurityManager let you do capabilities-like things? E.g. can you be given a SecurityManager as an argument, allowing code using that manager to temporarily access a file, while normally blocking it? Or is it something closer to "applies to a ClassLoader"? Though with enough effort and runtime cost you could make those equivalent, of course.