我找到了 90% 的答案。简短的回答是我们需要设置很多权限,但是这个过程很明显并且很容易隐藏在一些实用程序类中。如果它在某个标准的地方可用,那就太好了,但是哦,好吧。 乐叹息。
我们需要做的就是检查类路径和 ext 目录的系统属性(并为它们添加 FilePermissions(只读)),设置必要的 SecurityPermissions(只读),最后为所有系统添加 PropertyPermissions(只读)特性。剩下的就是一些非常明显的权限,例如,授予临时目录的 r/w/d(但不执行)权限,授予对 localhost 的“解析”访问权限等。
一个真正安全的沙箱可能不想让所有系统属性都可读,但这很容易修复,留给读者。
public class LoggingSecurityManager extends SecurityManager {
private AccessControlContext ctx;
private Properties properties = new Properties;
private Set missingProperties = new HashSet();
public LoggingSecurityManager() {
properties.add(
new FilePermission(System.get("java.io.tmpdir") + "/-", "read,write,delete"));
// maybe...
properties.add(
new FilePermission(System.get("user.home") + "/-", "read,write,delete"));
addSystemPropertyPermissions();
addSecurityPermissions();
addClassPathPermissions();
addOtherPropertyPermissions();
permissions.add(new RuntimePermission("accessClassInPackage.sun.reflect"));
permissions.add(new RuntimePermission("accessClassInPackage.sun.jdbc.odbc"));
permissions.add(new RuntimePermission("accessClassInPackage.sun.security.provider"));
permissions.add(new SocketPermission("localhost", "resolve"));
permissions.add(new NetPermission("getProxySelector"));
ctx = new AccessControlContext(new ProtectionDomain[] {
new ProtectionDomain(null, permissions)
});
}
/**
* Add read-only permission to read system properties.
* We may want to filter this list to remove sensitive information
*/
public void addSystemPropertyPermissions() {
for (Object key : Collections.list(System.getProperties().keys())) {
permissions.add(new PropertyPermission((String) key, "read"));
}
}
/**
* Add read-only permissions for initializing security.
*/
public void addSecurityPermissions() {
permissions.add(new SecurityPermission("getPolicy"));
permissions.add(new SecurityPermission("getProperty.random.source"));
permissions.add(new SecurityPermission("getProperty.securerandom.source"));
for (int i = 1; i < 10; i++) { // configurable limit?
permissions.add(new SecurityPermission("getProperty.security.provider." + i));
}
String s = Security.getProperty("securerandom.source");
if ((s != null) && s.startsWith("file:/")) {
permissions.add(new FilePermission(s.substring(5), "read"));
}
// should have been covered already but wasn't....
permissions.add(new FilePermission("/dev/random", "read"));
}
/**
* Add read-only permissions for everything on classpath.
*/
public void addClassPathPermissions() {
permissions.add(new FilePermission(String.format("%/lib/-",
System.getProperty("java.home")), "read"));
// add standard class path.
String pathSep = System.getProperty("path.separator");
for (String entry : System.getProperty("java.class.path").split(pathSep)) {
File f = new File(entry);
if (f.isFile()) {
permissions.add(new FilePermission(entry, "read"));
} else if (f.isDirectory()) {
permissions.add(new FilePermission(String.format("%s/-", entry), "read"));
} // or could be neither fish nor fowl
}
// add endorsed extensions.
for (String dir : System.getProperty("java.ext.dirs").split(pathSep)) {
permissions.add(new FilePermission(String.format("%s/-", dir), "read"));
}
}
/**
* Add other standard properties.
*/
public void addOtherPropertyPermissions() {
permissions.add(new PropertyPermission("jdbc.drivers", "read"));
permissions.add(new PropertyPermission("java.security.egd", "read"));
permissions.add(new PropertyPermission("socksProxyHost", "read"));
}
}
少了一点可怕。这些权限为大规模破坏打开了大门。
// ------------ S C A R Y - B L O C K -----------
permissions.add(new ReflectPermission("suppressAccessChecks")); (!!)
permissions.add(new RuntimePermission("createClassLoader")); (!!)
permissions.add(new SecurityPermission("putProviderProperty.SUN"));
permissions.add(new RuntimePermission("readFileDescriptor"));**
permissions.add(new RuntimePermission("writeFileDescriptor"));
// ------------ S C A R Y - B L O C K -----------
我还没有决定这里的最佳行动方案。我想我可以做的是覆盖 checkPermission 方法并在看到前两个权限(至少)时查看调用堆栈。如果它们来自 JDK 的深处,它们可能是安全的。如果来自用户代码,它们可能是不确定的。