SecurityManager deprecation and reflection with suppressAccessChecks

177 views Asked by At

I'm an university lecturer and I'm revising my lecture on Java reflection. Other years when teaching about the horrors of suppressAccessChecks I was showing that you could set up a SecurityManager and do something like

    if ("suppressAccessChecks".equals(p.getName())){
      StackTraceElement[] st = Thread.currentThread().getStackTrace();
      if(.. st ..) { throw new SecurityException(); }
    }

In this way you can allow for whitelisted deserializers only to call suppressAccessChecks. However, now they are deprecating the SecurityManager. I think the new module system is supposed to help here, but I'm failing to find resources explaining how to support the idea of the whitelisted deserializers above.

Any hint?

1

There are 1 answers

4
Holger On BEST ANSWER

With Java modules, setAccessible is already restricted, even without a security manager:

This method may be used by a caller in class C to enable access to a member of declaring class D if any of the following hold:

  • C and D are in the same module.
  • The member is public and D is public in a package that the module containing D exports to at least the module containing C.
  • The member is protected static, D is public in a package that the module containing D exports to at least the module containing C, and C is a subclass of D.
  • D is in a package that the module containing D opens to at least the module containing C. All packages in unnamed and open modules are open to all modules and so this method always succeeds when D is in an unnamed or open module.

If we assume a typical scenario of a module M using a persistence service in module P and the members are not accessible anyway, only the last bullet applies; M must open the package(s) to P to enable the access override.

This can be done via a qualified opens directive

module M {
  opens aPackage.needing.persistence to P;
}

This way, only the explicitly specified module(s), i.e. P, can use setAccessible for members of types in aPackage.needing.persistence.

In case of the HotSpot JVM, there’s the option --add-opens allowing to add qualified opens relationships at startup, in addition to declared ones, but there is no option for a module of an already running application to create such a relationship at runtime to gain additional access rights itself (unless the security is already subverted). It’s imaginable that other environments do not even support such a startup option.


It’s worth mentioning that there are still some new restrictions which can’t be circumvented this way. As also mentioned in setAccessible’s documentation:

This method cannot be used to enable write access to a non-modifiable final field. The following fields are non-modifiable:

  • static final fields declared in any class or interface
  • final fields declared in a hidden class
  • final fields declared in a record

See also this answer

In other words, in case of a record type, the persistence service still must use the constructor to deserialize an instance, even when suppressing access checks.