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?
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 amember
ofdeclaring class
D
if any of the following hold:
C
andD
are in the same module.- The member is
public
andD
ispublic
in a package that the module containingD
exports
to at least the module containingC
.- The member is
protected static
,D
ispublic
in a package that the module containingD
exports to at least the module containingC
, andC
is a subclass ofD
.D
is in a package that the module containingD
opens
to at least the module containingC
. All packages in unnamed and open modules are open to all modules and so this method always succeeds whenD
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.