The security manager mechanism in Java is great (Java 1.7 bugs notwithstanding): it gives you a simple, reliable way to restrict what different parts of your code can do. Most of the time, the standard SecurityManager implementation along with a carefully-defined security policy will meet your security needs, which is lovely. However, there are times when the standard SecurityManager implementation doesn't cut it. This post address a couple of those cases.
One of the main problems with the SecurityManager is that it introduces a performance overhead. This is because every time a restricted method is called, the SecurityManager has to check the call stack to make sure that the necessary permissions have been granted to the code in every frame. A lot of the time this isn't an issue, but if your application is performance-sensitive and makes a lot of calls to restricted functions, these checks can add up.
It's also not uncommon to find applications that only need to place restrictions on code running in specific threads: most of the threads in the application will only ever run trusted code, but one or two threads will be used to call some un-trusted libraries. The standard SecurityManager, when installed, applies to all code running in the JVM, even if the majority of the threads in the VM won't run un-trusted code. Even if you define your security policy such that your trusted code is granted all the permissions it needs, a lot of unnecessary checks will be made by the SecurityManager.
A nice solution to this is to define a custom SecurityManager that can be enabled and disabled programatically on a per-thread basis. This can be achieved pretty easily using ThreadLocal variables to store the enabled/disabled state of the SecurityManager on each thread:
It's also not uncommon to find applications that only need to place restrictions on code running in specific threads: most of the threads in the application will only ever run trusted code, but one or two threads will be used to call some un-trusted libraries. The standard SecurityManager, when installed, applies to all code running in the JVM, even if the majority of the threads in the VM won't run un-trusted code. Even if you define your security policy such that your trusted code is granted all the permissions it needs, a lot of unnecessary checks will be made by the SecurityManager.
A nice solution to this is to define a custom SecurityManager that can be enabled and disabled programatically on a per-thread basis. This can be achieved pretty easily using ThreadLocal variables to store the enabled/disabled state of the SecurityManager on each thread:
package demo; import java.security.Permission; public class SelectiveSecurityManager extends SecurityManager { private static final ToggleSecurityManagerPermission TOGGLE_PERMISSION = new ToggleSecurityManagerPermission(); ThreadLocalenabledFlag = null; public SelectiveSecurityManager(final boolean enabledByDefault) { enabledFlag = new ThreadLocal () { @Override protected Boolean initialValue() { return enabledByDefault; } @Override public void set(Boolean value) { SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { securityManager.checkPermission(TOGGLE_PERMISSION); } super.set(value); } }; } @Override public void checkPermission(Permission permission) { if (shouldCheck(permission)) { super.checkPermission(permission); } } @Override public void checkPermission(Permission permission, Object context) { if (shouldCheck(permission)) { super.checkPermission(permission, context); } } private boolean shouldCheck(Permission permission) { return isEnabled() || permission instanceof ToggleSecurityManagerPermission; } public void enable() { enabledFlag.set(true); } public void disable() { enabledFlag.set(false); } public boolean isEnabled() { return enabledFlag.get(); } }
The custom SecurityManager simply sub-classes the standard SecurityManager and overrides the checkPermisson methods so that a ThreadLocal boolean is tested to see if the check is necessary before it's carried out. The ThredLocal boolean can be set programatically via an enable/disable methods, and we make sure that only authorised code can enable/disable the security manager by checking a special ToggleSecurityManagerPermission in the set method of the ThredLocal. The ToggleSecurityManagerPermission class is very simple, and looks like this:
package demo; import java.security.Permission; public class ToggleSecurityManagerPermission extends Permission { private static final long serialVersionUID = 4812713037565136922L; private static final String NAME = "ToggleSecurityManagerPermission"; public ToggleSecurityManagerPermission() { super(NAME); } @Override public boolean implies(Permission permission) { return this.equals(permission); } @Override public boolean equals(Object obj) { if (obj instanceof ToggleSecurityManagerPermission) { return true; } return false; } @Override public int hashCode() { return NAME.hashCode(); } @Override public String getActions() { return ""; } }
That's pretty much all there is to it. You need to define a security policy that will ensure that any trusted code on the stack for threads where the SecurityManager will be enabled has the necessary permissions, and that trusted code has permission to enabled and disable the SecurityManager, but that's it. For a working example of the above with some simple tests, see the Github repository https://github.com/alphaloop/selective-security-manager.