Ask HN: Why isn't capability-based security more common?
Recent ["self-propagating NPM malware"](https://news.ycombinator.com/item?id=45260741) reminds us that the predominant security model is basically whack-a-mole: you gotta trust _every_ piece of software you run (including all the libraries, plugins, etc), unless you explicitly sandbox it.
Capability-based security might offer an alternative: software should not have access to things when it's not explicitly provided with access. I.e. "classic" desktop security is kind of a blacklist model (everything is possible unless explicitly restricted e.g. via sandbox) while capbility-based security is like a whitelist.
On a programming language level it's usually known as object-capability model, and there's a number of programming languages which implement it: https://en.m.wikipedia.org/wiki/Object-capability_model
The question: why isn't it more popular? It doesn't even seem to be widely known, let alone used. (Aside from isolated examples.)
Is there any chance it would be widely adopted?
I guess one objection is that people don't want to manually configure security. But perhaps it can be integrated into normal UX if we really think about it: e.g. if you select a file using a system-provided file picker it would automatically grant access to that file, as access is explicitly authorized.
#1 problem is the IT mindsets' unwillingness to adopt a default-deny philosophy.
Default-Accept philosophy make it easier for millions of holes to open up ag first and you spend entire IT budget locking down things you don't need but not the ones you don't see that needs closing.
Default-deny is one time IT expenditure. And you start poking holes to let things thru. If that hole is dirty, you plainly see that dirty hole and plug it.
All that also equally applies to CPU designers.
"Default deny" is the Windows model of clicking "yes" for the incessant permissions dialog box.
Or the Linux model of prefixing every command with a "sudo".
It doesn't work.
Well that happens when it's bolted onto something not designed for fine-grained access.
It's much different when UX is built around it. E.g. for a web browser _has_ to treat web pages as untrusted. So instead of giving web page access to file system (that would be equivalent of `sudo`) you selected individual files/directories using a trust browser UI, and they are made available through particular APIs which are basically equivalent to ocaps. So if don't need to support POSIX APIs ("I WANT TO JUST fopen!!!") it's much easier.
> It's much different when UX is built around it. E.g. for a web browser
Why don't you run th e web browser as init then ?
Nah, preserves current IT job security. #headduck
I don't know much (if anything) about it, but it can be turned into an interesting thought experiment.
Let’s use Apple as an example, as they tend to do major transitions on a regular basis.
So, let’s say that the top tier already approved the new security mode(l).
Now, how to do it?
My understanding is that most if not all APIs would have to be changed or replaced. So that's pretty much a new OS, that needs new apps (if the APIs change, you cannot simply recompile the apps).
Now, if you expose the existing APIs to the new OS/apps, then what's the gain?
And if you don't expose them, then you basically need a VM. I mean, I don’t know Darwin syscalls, but I suspect you might need new syscalls as well.
And so you end up with a brand new OS that lives in a VM and has no apps. So it's likely order(s?) of magnitude more profitable to just harden the existing platforms.
We don't have to theorize. MacOS has TCC, which was first added in 2012 in Mountain Lion and later versions added more grants. It's not a proper capability system, but it's still a security system that came after traditional Unix primitives. Later macOS versions added a sandbox as well, and programs can't reach outside of that sandbox willy nilly. So macOS programs can't navigate to ~/Photos without the user hitting allow, possibly in system preferences, not just as a pop-up.
The external APIs themselves haven't changed. Without recompiling, if I want to enumerate photos using the appropriate framework, the newer version of the framework code will ask the TCC system which popups up a "allow access to Photos" dialog. If my program tries to read the photo db file with a standard open(), it's going to get permission denied. (you can grant programs full disk access of you so choose.)
The more fine-grained you make a capability system, the more you have an explosion of the number of permissions required by an application, and the chance that some combination of permissions grants more access than intended.
It also requires rewriting all your apps.
It also might require hardware support to not be significantly slower.
"Just sandbox each app" has much fewer barriers to entry, so people have been doing that instead.
And systems like Android have been working with discrete permissions / capabilities, because they were able to start from scratch in a lot of ways, and didn't need to be compatible with 50 years of applications.
Capability-based security via sandboxing is not that hard to overlay.
Firejail provides a very usable implementation with default profiles for lots of programs.
I've been using this since the PyTorch exploit [1].
Most programs don't need access to large parts of your filesystem or network.
[1] https://pytorch.org/blog/compromised-nightly-dependency
In my experience, most sandboxing systems seem to be deficient in many ways. (It would be possible to improve it (I don't know what changes might be required in the kernel, although some things might be possible with ptrace, it would make it difficult and possibly inefficient and/or unportable), although making an entirely new computer design would improve the security more than that.)
Check out https://ocapn.org/
https://www.spritely.institute/
https://files.spritely.institute/papers/spritely-core.html
I had thought of some similar ideas, including the similar working of proxies, and I also had the same idea of how proxies could be used (although proxies have many other uses as well, not all of which are related to security), and also network transparency (which is also implemented by using proxies; it is not a feature that the kernel knows about). A program that receives a proxy does not know if it is a proxy or not (or what it is a proxy of); it can only send/receive messages.
I also had similar idea like calling programs in the command shell, although in mine, a program cannot even return a value without being given a capability to send the result to (although there may be syntactic sugar to handle this without needing to write that explicitly), and the result will always be the same if any input it receives is the same (and in the same order); a capability is even required to determine the current date/time, etc. (One of the forks of a program file would specify the expected type of the initial message, which the command shell can use for type checking and related stuff like that.)
Their "vats" are similar to my idea of how processes might be made, but different in many ways. A process consists of memory, which may include references to capabilities, and also includes the processing state. It does not necessarily use a event loop, although it can be implemented in that way. I did not consider using promises; I am unsure if it is necessary, which it might not be.
However, my idea uses native code (with its own instruction set) rather than Guile or Scheme or Lisp, and system calls will be used for doing I/O with capabilities (and there aren't many system calls for doing other things than that). (Emulation would probably be possible (directly of the instruction set, or of the command shell and other higher-level stuff, or a combination), although I also have a different keyboard layout and other things different from existing systems.)
Also, there are more things to be considered than related there. For example, nested transactions of multiple objects at once (even if they do not necessarily know each other), multiple locking, the hypertext file system model, etc.
I also would not use JSON and would not use Unicode.
I presume this is because of compatibility reasons.
Back in 70s and 80s computers didn't contain valuable information to care about and there was no Internet to transmit such information. So, adding some sort of security elements in operating systems had no sense. In these years modern operating system were first developed - Unix, Dos, Windows. Since then many architectural decisions of these operating systems weren't revised in order to avoid breaking backward-compatibility. Even if we need to break it to achieve better security, no one is ready to make such sacrifice.
There are projects of operating systems with focus on security, which are not just Unix-like systems or Windows clones. But they can't replace existing operating systems because of network effects (it's unpractical to use a system nobody else uses).
Capability based security is the natural way to do things in most other aspects of our lives.
We live in a capabilities based world. Most of my outlets give 120 volts at 15 amps and deny access to the whole power grid. We don't have to have certified electricians check each and every device before connecting it to power.
When I want to pay for a purchase I have a number of possible economic capabilities from 1 cent to 100 dollars. I don't have to risk accidentally draining my bank account to pay for a soda.
On our GUI oriented systems, file selections could automatically handle capabilities and the user might not even notice a difference. We shouldn't have to risk our computer just to run a program.
Give that all of this was worked out in the early 1980s, I strongly suspect a conspiracy to deliberately delay the implementation of capabilities by the US NSA and other Intelligence agencies. Ironically this choice resulted in horrible long term consequences for US National Security.
Have a look at microsoft MSIX.
I think that, if it is done well (which will require a new operating system design (and a new instruction set may also be helpful); I have many ideas), it will have more benefits than only security.
It is much like the object-capability model described on Wikipedia, and it is neither a blacklist model nor a whitelist model. A whitelist model would have a list (or other criteria) of what is permitted, but for a better capability-based system, it works differently.
My idea involves capabilities (which can include proxy capabilities as well; i.e. it is not necessarily what you asked for, but you can send/receive messages anyways) which can be sent and used like the object-capability model, including as the initial message (instead of using command-line arguments, file descriptors, etc, it uses the "initial message"; this is like the "initial conditions" described on Wikipedia), which should include some capabilities otherwise it cannot do any I/O and will be automatically terminated (before the program gets a chance to run) if no debugger is attached. (Unlike the standard object-capability model, there is no "endowment" (at least at the level of the operating system, although programming languages might have this feature); the capability does nothing by itself, but the process that created it can receive messages that itself and/or other processes send to it and use them for whatever that capability is intended to mean.)
Some benefits of a proxy capability system includes that many other things become unnecessary, since they can be done with proxies, such as: finer permissions than the capabilities normally allow, adding print preview to a program that does not have that feature (but that is able to print), simulating error conditions, testing if the date/time handling of a program would work correctly on February 29 (or some other date, or a leap second, etc) without needing to change the clock on your computer, accessing resources on another computer as though they are local (and vice-versa), etc.
You might be able to pass capabilities using the command shell like how on UNIX you can use pipes, but in this one you would pass capabilities instead (as well as the ability to create your own).
> I guess one objection is that people don't want to manually configure security.
In many ways, reasonable defaults can be configured, and some things can be configured automatically, although it would still be possible to manually set them to whatever you want them to be.
> if you select a file using a system-provided file picker it would automatically grant access to that file, as access is explicitly authorized
Nevertheless, there are problems with such things, especially if you try to add them to existing systems. For example, a program might require access to additional files depending on the one accessed (either referenced by those files or by a modification of the original file's name (e.g. the journal file in SQLite)), configuration files and command-line arguments, etc. You would also need to consider such things as popen (many of my programs use it, with a command specified by the user at run time or specified in a configuration file).
In a new operating system design you could also change the file system model as well, in order to help with this (I had ideas about how to do this, too).
This is something that needs to be baked into the operating system, which is not supported by major OSs today. The next best thing is to rely on a "secure environment" where applications can be installed and run, similar to phone apps or browser extensions. This environment would probably use application manifests to list entitlements (aka capabilities), like disk access, network access, etc. But until then, we're stuck with the ambient security model.
https://www.qubes-os.org/