Reputation: 734
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
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 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.
Upvotes: 5