5 ms·
> Going forward, the only reasonable assumption is that if you let a third party run their code in a process, no matter how you verify accesses or otherwise try
by kentonv 8y ago
> Going forward, the only reasonable assumption is that if you let a third party run their code in a process, no matter how you verify accesses or otherwise try to contain that code, you should assume it has a read access to the entire process.
If this is true, it is unbelievably bad for the future of security and computing in general. People throwing around this assertion are, in my opinion, not appreciating how bad it is. We need to try a lot harder before we give up.
Here's why:
1. Finer-grained isolation makes security better, because it allows us to apply the Principle of Least Authority to each component of the system, and protect components of a system from bugs in other components. To make meaningful gains in security going forward, we need to encourage more fine-grained isolation. If process-level isolation is the finest grain we'll ever have, we can't make these advances.
2. The scalability of edge computing requires finer-grained isolation than process isolation. The trend is towards pushing more and more code to run directly on devices or edge networks rather than centralized servers. That means that the places where code runs need to be able to handle orders of magnitude more tenants than before -- because everyone wants their code to run in every location. If we can't achieve high multi-tenancy securely -- by which I mean 10,000 or more independently isolated pieces of code running on the same machine -- then the only solution will be to limit these resources to big, trusted players. Small companies will but shut out from "the edge". That's bad.
Luckily, the "process isolation is the only isolation" claim is wrong. It's true that we need to evolve our approach to make sub-process isolation secure, but it's not impossible at all. In fact, it's possible to design a sandbox where you can trivially prove that code running inside it cannot observe side channels.
Here's how that works: Thank about Haskell, or another purely-functional language. An attacker provides you with a pure function to execute. Because it's a pure function, for some given set of inputs, it will always produce exactly the same output, no matter what machine you run it on, or what else is going on in the background. Therefore, the output cannot possibly incorporate observations from side channels, no matter what the code does internally.
So: It is possible to run attacker-provided code without exposing secrets from the process's memory space.
The question is, of course, how do we build a useful sandbox that relies on this property. There is a lot of work to be done there. Luckily, we don't really have to use a purely-functional language, we only need to use a deterministic language. It turns out that the world's most popular language, JavaScript, is actually highly deterministic, in large part due to its single-threaded nature.
We do need to remove access to timers, or find a way to make them not useful. We also need to prevent attackers from being able to time their code's execution remotely. Basically, we need to think carefully about the I/O surface while paying attention to timing. But sandboxing has always been about thinking carefully about the I/O surface, and we have a lot of control there. We just have a new aspect that we need to account for.
I think it's doable.
- jcranmer 8y ago> Here's how that works: Thank about Haskell, or another purely-functional language. An attacker provides you with a pure function to execute. Because it's a pure function, for some given set of inputs, it will always produce exactly the same output, no matter what machine you run it on, or what else is going on in the background. Therefore, the output cannot possibly incorporate observations from side channels, no matter what the code does internally. This is patently false. The attacker can do timing on his side and see how long it takes your service to return a response. If you let an attacker have any sort of output channel, you give him the power to use his stuff to find side channels, and there is nothing you can do to close it.
- kentonv 8y ago> The attacker can do timing on his side Yes, I mentioned that in my comment: > We also need to prevent attackers from being able to time their code's execution remotely. I don't think it's impossible to mitigate. First, not all use cases actually involve the attacker being able to directly invoke and then measure the timing of their code. Imagine, for example, that I'm using attacker-provided code to apply some image manipulation to a photo on my phone, then I post the photo online. The attacker doesn't have any way to know how long their code took to execute, and I can be confident that they haven't exfiltrated side channels through the image via steganography because their code was deterministic so couldn't have incorporated those side channels into its output. Second, I don't think the space of timer noise techniques has been adequately explored. Timer noise is one of the main defenses browsers have deployed against spectre, and pragmatically speaking it has been reasonable effective. Yes, in theory there are lots of statistical techniques an attacker might use to get around it, but it certainly increases the cost of attack. We need to figure out how to push the cost of an attack out-of-reach, even if we can't make it completely impossible.
- voidmain 8y agoDeterministic code cannot receive a side channel. It may, as you point out, be able to transmit on a side channel. It might even be able to "reflect" one side channel into another one. But these risks seem both less severe and more easily mitigated. At least, the history of attacks of the latter types is not very impressive.