Having implemented SecurityManager and Policy providers, I'd like to
comment on some of the assessments, some thoughts:
* Poor performance, this is specific to the Java Policy
implementation, I have addressed this in my implementations,
performance impact is imperceptible, I know how to address it.
* Brittle permission model - Agreed, it is brittle.
o Developers should use doPrivileged blocks to make security
sensitive calls, so only one domain is on the call stack. Agreed
when they don't do this it causes viral permission expansion.
o Implementing Deny would address the example given.
o Un-trusted code should never be run on the JVM, however some
code is more trusted, or well audited, so we may wish to limit
code that doesn't require privileges, based on principle of
least privilege.
o Is there a way to simplify Permission's? We can write tools to
generate policy files (I have).
* Difficult programming model
o If the library developer uses doPrivileged blocks and documents
the permissions, then the application code doesn't require those
permissions, as it is not on the call stack.
o Agreed that most developers don't do this.
o I use a tool to generate policy files that capture the viral
growth of permissions, these still require some editing, eg to
add system properties.
Is there a simpler way to limit permissions of library code?
I have gotten so used to secure coding, because we use it, I don't find
it difficult, writing non blocking or good concurrent code is more
difficult and developers make lots of mistakes with race conditions,
visibility and synchronization.
There are good static code analysis tools that identify poor coding
practices, the same tools could be updated to include secure coding
practices as well, to address viral permission expansion.
Perhaps if we instead address the performance and usability issues, we
could improve adoption,so it adds to Java's appeal, rather than
detracting from it?
Regards,
Peter.
Comments and code from one of my policy implementations (I have a few
policy implementations that use the decorator pattern to add
functionality, like dynamic permission grants):
* If there is sufficient interest, we can implement a DENY clause,
* in this case DENY cannot apply to GRANT clauses that contain
* {@link java.security.AllPermission}, the domains to which a DENY clause
* would apply will be a less privileged domain. For example a user
could be
* granted SocketPermission("*", "connect"), while a DENY clause might
* list specific SocketPermission domains that are disallowed, where a
DENY
* clause has precedence over all GRANT clause Permissions except for
AllPermission.
@Override
public boolean implies(ProtectionDomain domain, Permission
permission) {
if (permission == null) throw new
NullPointerException("permission not allowed to be null");
if (domain == myDomain) {
PermissionCollection pc = myPermissions;
return pc.implies(permission);
}
Class klass = permission.getClass();
// Need to have a list of Permission's we can sort if
permission is SocketPermission.
NavigableSet<Permission> perms = new
TreeSet<Permission>(comparator);
PermissionGrant [] grantRefCopy = grantArray;
int l = grantRefCopy.length;
/* Check for privileged grants first to avoid recursion when
* privileged domains become involved in policy decisions */
for (int j = 0; j < l; j++){
PermissionGrant ge = grantRefCopy[j];
if (ge.isPrivileged()){
if (ge.implies(domain)){
return true;
}
}
}
/* Merge the static Permissions, check for Privileged */
PermissionCollection staticPC = null;
if (domain != null) {
staticPC =domain.getPermissions();
if (staticPC != null){
Enumeration<Permission> e = staticPC.elements();
while (e.hasMoreElements()){
Permission p = e.nextElement();
// return early if possible.
if (p instanceof AllPermission ) return true;
// Only add relevant permissions to minimise size.
if (klass.isInstance(permission) || permission
instanceof UnresolvedPermission){
perms.add(p);
}
}
}
}
/* Check less privileged grants */
for ( int j =0; j < l; j++ ){
PermissionGrant ge = grantRefCopy[j];
if (!ge.isPrivileged()){
if (ge.implies(domain)){
Collection<Permission> c = ge.getPermissions();
Iterator<Permission> i = c.iterator();
while (i.hasNext()){
Permission p = i.next();
// Only add relevant permissions to minimise size.
if (klass.isInstance(permission) || permission
instanceof UnresolvedPermission){
perms.add(p);
}
}
}
}
}
return convert(perms).implies(permission);
}
On 28/04/2021 8:19 pm, Lim wrote:
On Wed, Apr 21, 2021 at 8:38 PM Ron Pressler <ron.press...@oracle.com> wrote:
Its current events might be not have everything you want, but will be expanded,
in
part to address the functionality that will be lost with the removal of
Security Manager.
I agree that monitoring needs to be improved since there is a lack of
monitoring APIs except for JFR. Until those monitoring APIs are on par
with the usage of SM "monitoring", it makes no sense to remove without
providing alternatives.
Libraries that can disable the Security Manager aren’t able to circumvent
OS-level
sandboxing. If you’re not afraid of that, then they’re trusted and JFR is
superior;
if they’re untrusted, then configuring the Security Manager correctly for
untrusted rich
libraries is very difficult.
Since you said that it cannot circumvent OS-level sandboxing, what
will prevent those
libraries to monitor if there is JFR active and become dormant until
there is no JFR
present, then it will execute the malicious behavior; Or, the library
attempts to hide
or render the JFR useless so that it will not be recorded and noticed?
On Wed, Apr 21, 2021 at 8:55 PM Ron Pressler <ron.press...@oracle.com> wrote:
For rich libraries and applications, your best bet is an OS-level sandbox. The
Security Manager
might give you a false sense of security.
Yes, OS-level sandbox is good but can it be scalable for many types of end
users that have different OS and hardware environments? In addition, how many
end users out there have used sandbox to isolate their desktop applications
except if the program has built-in sandbox such as web browsers? Programs
such as Docker does not count.