7 ms·
Could you solve this in Java using the SecurityManager stuff that was used to sandbox applets, or is all that considered broken these days? (I'm not sure if you
by ptx 6y ago
Could you solve this in Java using the SecurityManager stuff that was used to sandbox applets, or is all that considered broken these days? (I'm not sure if you can different SecurityManagers for different parts of the app though.)
- ryukafalz 6y agoI'm not familiar enough with Java to have a strong opinion on this, but this HN comment from the linked article mentions that you can only have one SecurityManager per app, so sounds like that's still too coarse-grained: https://news.ycombinator.com/item?id=18599365 https://news.ycombinator.com/item?id=18599365
- mindcrime 6y agoIn my experience, the biggest problem with the Java SecurityManager approach is that it's thought of as too difficult to understand / cumbersome to configure (and I'm not saying this belief is wrong), and so most apps either run with no SecurityManager explicitly configured or configure things the "simplest possible way" which usually winds up being approximately equivalent to "anybody can do anything".
- avereveard 6y agoa security manager can examine the call stack and know which class from which package is asking to perform a privileged action within the app; the class object will tell you which loader loaded it, and you can ask the loader the physical location of where the class comes from if you want to be really sure no one has overloaded it.
- mindcrime 6y agoYes, it's a powerful and flexible mechanism. The problem is - how many people know (in detail) how to configure all of this power so as to take best advantage of it? My subjective perception is a that the answer is something like "a very small percentage of Java developers".
- nmadden 6y agoOracle’s own secure coding guidelines for Java [1] actually now recommend adopting a capability-based approach rather than relying on SecurityManager: > FUNDAMENTALS-5: Minimise the number of permission checks Java is primarily an object-capability language. SecurityManager checks should be considered a last resort. (Note: quite a lot of Java’s standard library is not designed along object-capability lines so you should take this advice with a pinch of salt). [1]: https://www.oracle.com/java/technologies/javase/seccodeguide.html https://www.oracle.com/java/technologies/javase/seccodeguide...
- mike_hearn 6y agoThey are not in tension. The Java security architecture is a mix of capability and module-level security. It's probably worth posting a quick refresher. The system is old but people don't use it much these days, and the documentation isn't that good. At one point I wrote a small JavaFX PDF viewer that sandboxed the PDF rendering code, to learn the system. I lost the source code apparently, but the hard part wasn't coding it (only a small bit of code was required), it was learning how to configure and use it. I tested the sandbox by opening a PDF that contained an exploit for an old, patched security bug and by using an old, vulnerable version of the PDFbox library. The sandbox successfully stopped the exploit. Fortunately the Java team still maintain the sandbox and via new technology like the module system and GraalVM, are reinforcing it. In fact, GraalVM introduces a new sandboxing technology as well that's simpler to use than the SecurityManager, however, it's also probably less appropriate for the case of blocking supply chain attacks. Java's internal security is based on two key ideas: 1. Code that can protect its private state. When the SecurityManager is enabled and a module is sandboxed, it isn't allowed to use reflection to override field or method visibility. 2. Stack walks. Let's tackle these backwards. Imagine it's time to do something privileged, like open a file. The module containing the file API will be highly privileged as it must be able to access native code. It will have a method called read() or something like that. Inside that method the code will create a new permission object that represents the permission to open files under a certain path. Then it will use AccessController, like this: FilePermission perm = new FilePermission("/temp/testFile", "read"); AccessController.checkPermission(perm); The checkPermission call will then do a stack walk to identify the defining module of every method on the stack. Each module has its own set of granted permissions, the access controller will intersect them to determine what permissions the calling code should have. Note: intersection. That means if any unprivileged code is on the stack at all the access check fails and checkPermission will throw an exception. For example, if an unprivileged module registers a callback from a highly privileged module, that doesn't work: the low privileged module will be on the stack and so the privilege is dropped. Access control contexts are themselves reified as objects, so instead of doing a permission check immediately you can 'snapshot' the permissions available at a certain point and use it later from somewhere else. And, starting a thread copies the permissions available at that point into the new thread context. So you cannot, in the simple case, elevate privilege. Stack walking and permission intersection is slow. It was optimised a lot in Java 9 and 10 so the performance impact of enabling sandboxing is much less than it once was, but it's clearly not zero overhead. Therefore the JVM provides other techniques. One is the notion of a capability, known from many other systems. Instead of doing a permission check on every single file read (slow), do it once and then create a File object. The File object allows reading of the underlying native file via its private fields. Whoever has a pointer to the File object can thus read from it. Because pointers cannot be forged in Java, this is secure as long as you don't accidentally lose your pointer or pass it to code that shouldn't have it. Sometimes you need to wrap a privileged operation to "dilute" it somehow. For example, imagine you have a module that allows arbitrary socket access. You also have an HTTP client. You would like the HTTP client to have network access, but for it to be usable by other modules that should only be able to contact specific hosts. Given what I've described so far that wouldn't work: the highly privileged code that can do native calls would do a stack walk, discover the unprivileged module on the stack and throw an exception. But there's a fix: AccessController.doPrivileged. This is kind of like sudo. It takes a lambda and truncates the stack that's examined for access checks at the point of use. Therefore it allows a module to use its own assigned permissions regardless of who is calling it. Of course, that is powerful and must be used carefully. In this case the HTTP client would itself check a different, HTTP specific permission. If that permission passed, then it would assert its own power to make arbitrary network connections and go ahead and use the lower level API. There are a few more pieces but they aren't core. One is the class called SecurityManager. This is the most famous part of the API but in fact, it's no longer really needed. SecurityManager simply delegates to AccessController now. Its API is slightly more convenient for the set of built in permissions. For the purposes of understanding the design you can effectively ignore it. The SecurityManager needs to be activated using a system property as otherwise, for performance reasons, permission checks are skipped entirely at the check sites. Beyond that it can be left alone, or alternatively, customised to implement some unusual security policy. Another piece is the policy language. Permissions are not intrinsic properties of a module in the JVM but rather assigned via an external file. The final piece is the module system. This isn't relevant to the sandbox directly, but it makes it easier to write secure code by adding another layer of protection around code to stop it being accessed by stuff that shouldn't have access to it. After a careful review of the old JVM sandbox escapes from the applet days, the Java team concluded that the module system would have blocked around half of them. So as you can see the design is very flexible. There's really nothing else like it out there, except maybe .NET CAS but I believe they got rid of that. Unfortunately there are some pieces missing, if we want to re-awaken this kraken. The first is that modules have no way to advertise what permissions they need to operate. That has to be specified in an external, per-JVM file, and there are no conventions for exposing this, therefore build tools can't show you permissions or integrate the granting of them. The second is that some code isn't sandbox compatible. The most common reason for this is that it wants to reflectively break into JVM internals, for example to get better performance. Of course that's not allowed inside a sandbox. A third is that some code isn't secure when sandboxed because it will, for example, create a File object for your entire home directory and then put it into a global public static field i.e. it doesn't treat its capabilities with care. The module system can help with this because it can make global variables less global, but it's still not ideal. The final piece is some sort of community consensus that sandboxing matters. Bug reports about sandboxing will today mostly be ignored or closed, because developers don't understand how to use it and don't see the benefit. It's fixable with some better tutorials, better APIs, better tooling etc. But first people have to decide that supply chain attacks are a new thing that matters and can't be ignored any longer.
- jjav 6y agoYes, with Java you can. That's how the web/application server containers worked (probably still do, but I've been disconnected). The server classes have different permissions from the application code classes (loaded from the .war/etc files). If an application code method calls into a system class, the permissions which apply are those or the application since that method is in the calling stack frame. I wrote this support into several Java web container and J2EE application server products back in the day. AFAIK, all that still works great today in Java.