Several question about JEP 486
Peter Firmstone
peter.firmstone at zeus.net.au
Mon Oct 7 12:00:20 UTC 2024
Hi Lim,
You raise a good point about how to prevent loading unauthorized code.
I'm interested to see the responses you'll get.
I think OpenJDK was overly focused on sandboxes, applets and
serialization, instead of authorization and principles of least
privilege, where the focus should have been, when it mattered. I guess
applets were a lucrative business once, it's a stark lesson...
Java never really prevented unauthorized code from loading, as it had a
"sandbox" everything was safe to run, that assumption turned out to be
false (Serialization breaking encapsulation made security a game of
whack a mole) and signing jar's was a way to elevate privileges.
As far as I can tell, support for authorization will be removed, and the
only new meagre implementations available are only intended to replace
Subject call functionality that currently depends on AccessController.
It's worth mentioning a lot of the functionality I'm about to discuss
will no longer be possible to support from Java 24 onward. We would
need to maintain some kind of fork and patch in our own authorization
code as low level support from the vm is required, this would only
require a small team of people, as it wouldn't be subject to corporate
regulation and we already have a lot of code that would make big
improvements, can't be called Java compatible though, as it would be non
compliant.
This permission was misnamed, it should have been called
ClassLoadPermission, controls on class loading was started around 20
years ago, when Sun still maintained Jini:
https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/net/jini/loader/DownloadPermission.java
It's worth noting we disable Java deserialisation, and if you want it to
stay disabled, you need to try deserialise something, because Java's
deserialisation protection mechanism is lazily initialized. This
permission works with our serialization implementation, it can't
deserialise circular object graphs either for security reasons.
Basically the source of the data, the user providing the data needs to
be authenticated and granted permission before it can be used. It's
hardened against attack, but we still advised against use on untrusted
data sources. Without the hook for this permission check, it wouldn't be
possible to prevent it using policy.
https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/org/apache/river/api/io/DeSerializationPermission.java
This package, although deprecated, provides some historical context:
https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/net/jini/security/proxytrust/package.html
We basically locked down the jvm with authentication, encryption and
authorization, with as little impact on performance and scalability as
possible. JGDMS is a modern evolution of Jini, but with IPv6 for end
to end connectivity, class resolution issues solved, no longer using
Java serialization or RMI, concurrent, high scaling with full support
for operating over insecure networks.
At a low level, when two remote systems first start communicating with
each other through dynamic discovery, all data is checksummed, all code
is checksummed or signed and the connections are authenticated and
connections are encrypted. Users at both ends are logged in and the
threads they invoke run with their Subject, in both local and remote
machines, including listeners, so events work seamlessly. Since proxy
code is loaded dynamically, it is isolated with ClassLoader visibility,
unique to the remote service's authenticated identity, even if third
party code is transmitted via a network connection, before it is loaded,
its source will be authenticated, and it will be loaded into a
ClassLoader specific to its authenticated identity. Service
implementations and local code interacts using compatible service
interfaces, constraints are placed on those services, by both endpoints,
if constraints aren't satisfied, then a connection is not established.
Authorization is dynamic, permissions are granted dynamically following
authentication and revoked typically following garbage collection. We
have a policy tool that's used to generate POLP policy files for
deployment and auditing. Permissions are granted to users, but only if
they're using the code we want them to, we can't trust them to use those
privileges running someone else's code.
https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-url-integrity/src/main/java/net/jini/url/httpmd/Handler.java
Some examples of dynamically generated POLP policy files:
https://github.com/pfirmstone/JGDMS/blob/trunk/qa/harness/policy/defaultsecuretest.policy
https://github.com/pfirmstone/JGDMS/blob/trunk/qa/harness/policy/defaultsecurephoenix.policy.new
One of the issues with Java, was the trusted platform has grown too
large, Java typically assumes all platform code is trusted (not good),
but as you can see in policy files, we're restricting a numerous
platform files, the only reason we can't reduce the trusted platform
further, is because platform code has a null ProtectionDomain.
Enforcing the principle of least privilege wasn't necessarily complex,
at least not after we did a lot of work implementing tooling.
Basically, you have your policy files, keystores and configuration
files. The software can be run without security in test environments,
then all that's necessary is a configuration change, keystore
configuration and policy files to enable it.
It's worth mentioning we removed DNS as part of the trusted security
authorization mechanism (due to risks from DNS spoofing), and from
SecureClassLoader:
https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/org/apache/river/api/net/RFC3986URLClassLoader.java
We also added an immutable RFC3986 Uri implementation that supports
RFC5952 IPv6 address normalization, under the hood it uses bit shift
operations, we found case conversion during normalization was a hotspot:
https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-platform/src/main/java/org/apache/river/api/net/Uri.java
Security doesn't impact scalability or performance, we load remote
classes much faster than standard java too, because we avoid DNS calls
that occur in CodeSource.equals() and SecureClassLoader calls.
Java has developed a reputation as being insecure (if you ask the
average it person), somewhat ironic, given it had the only authorization
framework that really worked, but I guess programmers themselves and
those determining project budgets have themselves to blame for not
utilising it, soon it will be gone.
--
Regards,
Peter
On 3/10/2024 8:30 pm, Lim wrote:
> Hi, I have some questions about this JEP.
>
>
> Will something similar to Python's audit hooks[1][2] be considered, to
> give transparency what is happening inside,
> so that jvm behavior can be monitored - such as a security agent (it
> can interact with Antimalware Scan Interface (AMSI)[3]).
> Currently without using JFR, all the operations in the JDK are
> essentially a blackbox.
>
> [1] https://peps.python.org/pep-0578/
> [2] https://docs.python.org/3/library/audit_events.html#audit-events
> [3] https://learn.microsoft.com/en-us/windows/win32/amsi/how-amsi-helps
>
> Again, this is not sandboxing as in "Handler API", described in the
> alternative section in the JEP.
>
> I know that JFR has some events for file/network but it has limited
> coverage and events are delayed,
> making it not suitable for auditing in this context.
> See https://mail.openjdk.org/pipermail/hotspot-jfr-dev/2021-May/002714.html
> for discussion.
>
>
>
> Secondly, historically it is possible to disallow unsigned jar to run
> in the applet era.
>
> For centrally managed devices, while java is installed system wide in
> developers' machines,
> management do only allow signed jar to be executed. While AppLocker[4]/SRP is to
> restrict java executable itself from running, it does not know if the
> jar executed is signed.
>
> Other languages such as Powershell can only allow signed scripts to
> run [5][6] with an example for signing [7].
> Is the default java[w] launcher able to restrict jar from executing be
> available for this scenario?
>
> [4] https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/appcontrol-and-applocker-overview#app-control-for-business
> [5] https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-7.4#-executionpolicy
> [6] https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_signing?view=powershell-7.4#to-permit-signed-scripts-to-run
> [7] https://adamtheautomator.com/how-to-sign-powershell-script/
>
>
>
> Thirdly, it is advised to use an agent to modify classes that call
> System::exit in the appendix section.
>
> There are libraries that have DRM checks at runtime to enforce certain
> restrictions such as licensing checks,
> to deliberately crash if an agent is found, or its class is tampered
> by the agent. (These checks can also be added by obfuscator)
>
> How does one handle such cases?
> Will the JDK offer to hide all the loaded agents or do I need to start
> modifying System::exit instead if this is the case?
>
>
> Thank you.
More information about the security-dev
mailing list