Search code examples
java-modulejava-security-managerjava-19

SecurityManager deprecation and reflection with suppressAccessChecks


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?


Solution

  • 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.