4 ms·
Instead of securing the "chain", we should instead isolate every library we import and run it under a sandbox. We should adopt the model of QubesOS. It follows
by udev4096 11mo ago
Instead of securing the "chain", we should instead isolate every library we import and run it under a sandbox. We should adopt the model of QubesOS. It follows security by isolation. There are lots of native sandboxing in linux kernel. Bubblewrap, landlock, gvisor and kata (containers, not native), microVMs, namespaces (user, network), etc
- whytevuhuni 11mo agoI don't know what the next programming language after Rust will look like, but it will definitely have built-in effects and capabilities. It won't fix everything (see TARmageddon), but left-pad-rs's build.rs file should definitely not be installing a sudo alias in my .bashrc file that steals my password when I cargo build my project.
- bluGill 11mo agoI hope you are right, but fear that there is no way to make such a thing that is usable. You likely end up with complex permissions that nobody understands and so you just "accept all", or programs that have things they must do under the same protection as the evil thing you want to block.
- marcosdumay 11mo ago> but fear that there is no way to make such a thing that is usable The function declarations declare every action it can do on your system, and any change adding new ones is a breaking change on the library. We've knew how to do it for ages. What we don't have is a good abstraction to let the compiler check them and transform the actions into high-level ones as they go through the stack.
- bluGill 11mo agoYou end up just declaring you can do anything because every time a function legitimately needs to do something every other function up the chain needs to be updated to do that same thing (even if in practice that can never happen but the compiler can't prove it). Like you say we don't have a good abstraction for this.
- marcosdumay 11mo ago> every time a function legitimately needs to do something Hopefully, you know exactly what a function needs to do when you write it. > every other function up the chain needs to be updated There are types reuse and composition to deal with that. Many languages with advanced type systems do composition badly, but it's still there.
- bluGill 11mo agoThe project I work on is 15 years old. A number of functions have got more ability over the years. Low level not so much but mid and high level a lot has changed from our oririnal plan
- marcosdumay 11mo agoThe ones near the top changing abilities is perfectly fine, both as a design rule and as them not leading to cascading changes in declarations through all your code. The problem is on the ones deep down. And we don't have very good tools for dealing with them, but they are perfectly workable even with what we have today. (A second large problem is on frameworks running on the top-level of your app. We have some tools for dealing with those, but the situation here is way worse than for libraries.)
- darrenf 11mo agoCan't help but think that Perl's tainted mode (which is > 30yrs old) had the right idea, and it's a bit strange how few other languages wanted to follow its example. Quoting `perldoc perlsec`: You may not use data derived from outside your program to affect something else outside your program--at least, not by accident. All command line arguments, environment variables, locale information (see perllocale), results of certain system calls ("readdir()", "readlink()", the variable of "shmread()", the messages returned by "msgrcv()", the password, gcos and shell fields returned by the "getpwxxx()" calls), and all file input are marked as "tainted". Tainted data may not be used directly or indirectly in any command that invokes a sub-shell, nor in any command that modifies files, directories, or processes, with the following exceptions: [...]
- lmm 11mo agoThat relies on blacklisting all the dangerous APIs which is inherently unreliable. Whitelisting safe things is safer, and the whole strongly typed functional programming world goes that way.
- yupyupyups 11mo agoIf there is a kernel level feature to throw sections of a process memory into other namespaces then yes, that may work. If you mean running a xen hypervisor for sqlite.so, then no thanks.
- criemen 11mo ago> we should instead isolate every library we import and run it under a sandbox I don't see how that'd be possible. Often we want the library to do useful things for the application, in the context of the application. What would incentivize developers to specify more fine-grained permissions per library than the union of everything their application requires? I see more use in sandboxing entire applications, and giving them more selective access than "the entire user account" like we do these days. This is maybe more how smartphones operating systems work than desktop computers?
- immibis 11mo agoIn languages without ambient I/O capabilities, it's not as hard as it sounds if you're used to languages with them. Suppose the only way you can write a file is if I give you a handle to that file - then I know you aren't going to write any other files. Of course, main() receives a handle from the OS to do everything. If I want you to decode a JPEG, I pass you an input stream handle and you return an output memory buffer; because I didn't give you any other capabilities I know you can't do anything else. Apart from looping forever, presumably. It still requires substantial discipline because the easiest way to write anything in this hypothetical language is to pass the do-everything handle to every function. See also the WUFFS project: https://github.com/google/wuffs https://github.com/google/wuffs - where things like I/O simply do not exist in the language, and therefore, any WUFFS library is trustworthy. However, it's not a general-purpose language - it's designed for file format parsers only.
- criemen 11mo agoFair enough, it makes more sense in a, say, Haskell-style pure functional language. Instead of getting a general IO monad, you pass in more restricted functionality. Still, it'd be highly painful. Would it be worth the trade-off to prevent supply chain attacks?
- immibis 11mo agoIt prevents a lot more than just supply-chain attacks. Give the unzipper a subdirectory handle with parent disabled, and it won't be vulnerable to directory traversal attacks, no matter how badly it's written.
- cesarb 11mo ago> Instead of securing the "chain", we should instead isolate every library we import and run it under a sandbox. Didn't we have something like that in Java more than a decade ago? IIRC, you could, for instance, restrict which classes could do things like opening a file or talking to the network. It didn't quite work, and was abandoned. Turns out it's hard to sandbox a library; the exposed surface ended up being too large, and there were plenty of sandbox escapes. > There are lots of native sandboxing in linux kernel. Bubblewrap, landlock, gvisor and kata (containers, not native), microVMs, namespaces (user, network), etc What all of these have in common, is that they isolate processes, not libraries. If you could isolate each library in a separate process, without killing performance with IPC costs, you could use them; one example is desktop thumbnailers, which parse untrusted data, and can use sandboxes to protect against bugs in the image and video codec libraries they use.
- lmm 11mo ago> Didn't we have something like that in Java more than a decade ago? IIRC, you could, for instance, restrict which classes could do things like opening a file or talking to the network. > It didn't quite work, and was abandoned. Turns out it's hard to sandbox a library; the exposed surface ended up being too large, and there were plenty of sandbox escapes. The number of escapes is exaggerated. The issue was that it didn't have capabilities or anything like that, so it wasn't really used - you couldn't say "library X should be able to access files that the application has passed into it but not other files", you had to say "class X.Y.Z should be able to access the file /foo/bar/baz.jpeg" in your security policy. So it was unusably brittle and everyone just said "all code can access all files"
- warkdarrior 11mo agoOne downside of fine-grained sandboxing is the overhead, as each (function) call into a library now has to cross sandbox boundaries. Even if we assume overhead is magically brought to zero, the real challenge is customizing the permission policy for each sandbox. I add, say, 5 new dependencies to my program, and now I have to review source code of each of those dependencies and determine what permissions their corresponding sandboxes get. The library that connects to a database server? Maybe it also needs filesystem access to cache things. The library that parses JSON buffers? Maybe it also needs network access to download the appropriate JSON schema on the fly. The library that processes payments? Maybe it also needs access to location information to do risk analysis. Are all developers able to define the right policies for every dependency?
- zzo38computer 11mo agoI think using proxy capabilities would partially mitigate this issue, and effectively they would work by using interfaces rather than permissions. By itself, it will not do; documentation will also be helpful, as well as deciding what you will need for your specific application (which are helpful even independently of such things as this).
- zzo38computer 11mo agoIt might work if you run the library in a separate process. This will work better on a capability-based system (with proxy capabilities; this would have other benefits as well and not only security) than on Linux, although it might still be possible to implement. An alternative way to implement library sandboxing would be to implement the sandboxing at compile time, which would work on any system and not require a separate process, and has both advantages and disadvantages compared with the other way.
- hinkley 11mo agoDocker does many things but 'convince code it's not running in a sandbox' ain't one of them. Nasty code can wait patiently for the right environment to get up to mischief.