Last active
October 11, 2023 19:29
-
-
Save beargiles/69f34af3bec7cde3a0c2bddb1409f3b5 to your computer and use it in GitHub Desktop.
Example of using AccessControlContext to restrict access to sensitive resources
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Permissive SecurityManager | |
* | |
* This SecurityManager can still be highly restricive but it allows | |
* access to a sensitive service. | |
*/ | |
public class PermissiveSecurityManager extends SecurityManager { | |
private final List<String> hosts = new ArrayList<>(); | |
public PermissiveSecurityManager(String... hosts) { | |
hosts.addAll(Arrays.asList(hosts)); | |
} | |
/** | |
* Simple check for authorized host + port. In practice we would | |
* want to check both host and port, e.g., using a reference set | |
* of NetPermissions. | |
*/ | |
public void checkConnect(String host, int port) { | |
if (hosts.contains(host)) { | |
throw new SecurityException("unauthorized host: " + host); | |
} | |
if (port != 443) { | |
throw bew SecurityException("unauthorized port: " + port); | |
} | |
} | |
public void checkPermission(java.security.Permission permission) { | |
System.out.println("permissive check!"); | |
// allow | |
} | |
/** | |
* Defer to AccessControlContext | |
*/ | |
public void checkPermission(java.security.Permission permission, Object ctx) { | |
System.out.println("permissive check with ctx!"); | |
((java.security.AccessControlContext) ctx).checkPermission(permission); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Example of using AccessControlContexts to restrcit access | |
* to sensitive resources. | |
* | |
* This is desirable since it allows us to ensure all access | |
* to the sensitive resource is identified and can be carefully | |
* audited. Without the SM/ACC it would be possible for a naive | |
* developer (or rogue actor) to add code that introduces a | |
* security vulnerability. | |
* | |
* We should still use static and dynamic vulnerability analysis, | |
* of course, but by explicitly limiting the access to PrivilegedActions | |
* it is easier to consider additional steps such as signed jars or | |
* sealed classes for the sensive code without adding that burden | |
* to the rest of the code base. | |
* | |
* IMPORTANT - THIS DOESN"T (CURRENTLY) WORK DUE TO FAILURE OF | |
* ACC.checkPermission(). I don't know if that's because of my | |
* current JVM or if I've overlooked a step in creating it. However | |
* the general idea still applies. | |
*/ | |
public class RestrictedAccessWithACC { | |
private final String host = "cnn.com"; | |
public static void main(String[] args) throws Exception { | |
final URL url = new URL("https://" + host + "/"); | |
// create and use permissive security manager | |
final SecurityManager permissive = new PermissiveSecurityManager(host); | |
System.setSecurityManager(permissive); | |
// this shows unexpected bahavior | |
sanityCheck(permissive); | |
// Cache this SM's ACC. | |
final AccessControlContext ctx = (java.security.AccessControlContext) permissive.getSecurityContext(); | |
// create and use restrictive security manager | |
final SecurityManager restrictive = new RestrictiveSecurityManager(); | |
System.setSecurityManager(restrictive); | |
// this should fail since it uses the restrictive ACC. | |
final SensitiveNetworkAccess access = new SensitiveNetworkAccess(url); | |
try { | |
java.security.AccessController.doPrivileged(access); | |
} catch (SecurityException e) { | |
System.err.printf("security exception! %s\n", e.getMessage()); | |
} | |
// this should successed since it uses the permissive ACC. However it | |
// fails since ctx.checkPermission() is failing. | |
try { | |
java.security.AccessController.doPrivileged(access, ctx); | |
} catch (SecurityException e) { | |
System.err.printf("security exception! %s\n", e.getMessage()); | |
} | |
} | |
/** | |
* Sanity check - it shows AccessControlContext.checkPermission() is | |
* not acting as expected. | |
*/ | |
static void sanityCheck(SecurityManager permissive) { | |
final Permission getCookieHandlerPermission = new NetPermission("getCookieHandler"); | |
// this should work since the current SecurityManager is 'permissive' | |
final AccessControlContext ctx = AccessController.getContext(); | |
// this explicitly creates the ACC from the security manager | |
final AccessControlContext obj = (AccessControlContext) permissive.getSecurityContext(); | |
// these work, as expected | |
permissive.checkPermission(getCookieHandlerPermission); | |
permissive.checkPermission(getCookieHandlerPermission, obj); | |
permissive.checkPermission(getCookieHandlerPermission, ctx); | |
// these don't't work even though though permission.checkPermission() succeeeded. | |
ctx.checkPermission(getCookieHandlerPermission); | |
obj.checkPermission(getCookieHandlerPermission); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Restrictive SecurityManager | |
* | |
* A restrictive SecurityManager, e.g., it might block ALL access to the | |
* network in order to prevent data exfiltration. | |
*/ | |
public class RestrictiveSecurityManager extends SecurityManager { | |
/** | |
* Explicitly whitelist. | |
*/ | |
public void checkPropertiesAccess() { | |
// allow | |
} | |
/** | |
* Explicitly whitelist | |
*/ | |
public void checkPropertyAccess(String key) { | |
// allow | |
} | |
/** | |
* Allow a few permissions but defer to parent SecurityManager | |
* for most things. | |
*/ | |
public void checkPermission(Permission perm) { | |
if (!(perm instanceof ReflectPermission) | |
&& !(perm instanceof SecurityPermission) | |
&& !(perm instanceof RuntimePermission) | |
&& !(perm instanceof FilePermission)) { | |
super.checkPermission(perm); | |
} | |
} | |
/** | |
* Defer to AccessControlContext | |
*/ | |
public void checkPermission(Permission perm, Object ctx) { | |
if (!(perm instanceof ReflectPermission) | |
&& !(perm instanceof SecurityPermission) | |
&& !(perm instanceof RuntimePermission) | |
&& !(perm instanceof FilePermission)) { | |
super.checkPermission(perm, ctx); | |
} | |
((java.security.AccessControlContext) ctx).checkPermission(perm); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Simple net client | |
* | |
* Simple net client - we just need something that requires the | |
* `getConnect()` permission. | |
* | |
* In practice we'll often use lambda functions instead of | |
* separate classes. | |
*/ | |
public class SensitiveNetworkAccess implements PrivilegedAction<byte[]> { | |
private final URL url; | |
public NetworkAccess(URL url) { | |
this.url = url; | |
} | |
public byte[] run() { | |
try (InputStream is = url.openStream()) { | |
return is.readAllBytes(); | |
} catch (IOException e) { | |
System.err.println("IOException!: " + e.getMessage(); | |
} | |
return new byte[0]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment