Marco Servetto
Marco Servetto

Reputation: 734

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?

Upvotes: 2

Views: 361

Answers (1)

Holger
Holger

Reputation: 298153

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.

Upvotes: 5

Related Questions