A possible JEP to replace SecurityManager after JEP 411
Peter Firmstone
peter.firmstone at zeus.net.au
Thu Apr 28 05:25:05 UTC 2022
Hi Martin,
Your arguments are the reasons why we use the principle of least
privilege. It creates a headache for attackers, similar to the
developer who's enabled SM for the first time and must manually add
every required permission for their software to function (who thought
that was a good idea lol?). The attacker requires an intimate
knowledge of the permissions their attack vectors or gadgets require,
including those a thread of execution has already been granted as well
as the features that those permissions will grant the attacker access
to. If the thread of execution doesn't have all required permissions,
it will cause the jvm to exit with a SecurityException. How does the
attacker obtain all the required information? With great difficulty.
As soon as the software does something a generated polp policy file
hasn't captured, a security exception is almost inevitably thrown, even
if it wasn't designed to protect an intended target, it almost
inevitably gets in the way. You will find that it's almost impossible
to do anything unintended. Although once you can impersonate a user or
service, say with the recent ECC exploit, you can at least do what that
user or service is allowed to do, but it still won't allow the attacker
to achieve their intended end goal unless the user or has all the
required permissions. In our case if the attacker can impersonate a
service, then they can load code, that's a problem, as our software
assumes ECDSA provides strong confidentiality. We recognise that once
you get to the stage of loading code into the jvm, it's pretty much game
over. A polp policy file won't defend against the recent ECC exploit.
https://github.com/pfirmstone/JGDMS/blob/trunk/JGDMS/jgdms-jeri/src/main/java/net/jini/jeri/ssl/ConfidentialityStrength.java
What polp can protect against however, is an exploit in a feature that
you don't use, it protected against the recent Log4J vulnerability.
An example of a polp policy file:
https://github.com/pfirmstone/JGDMS/blob/trunk/qa/harness/policy/defaultsecuresharedvm.policy
One of the improvements we can make (when re-implementing access
controls), is to reduce the size of the jdk's trusted computing base,
instead of having many trusted protection domains with all permission
(characterised with a null protection domain), we can give each module a
separate protection domain identity, and limit each only to the
permissions required for our software to function as intended. This
means that jvm modules we don't use will have no permissions at all. To
get around the large trusted jdk code base, we provided two methods
which append a ProtectionDomain, with only the user's required
permissions, it also prevents injection of a user Subject's permission's
into less privileged service domains. It's use hasn't really caught on
though, no doubt due to complexity.
https://github.com/pfirmstone/JGDMS/blob/c1edf5892306f24f8f97f459f499dec54984b08f/JGDMS/jgdms-platform/src/main/java/net/jini/security/Security.java#L590
This is really a hack because the jdk's trusted computing base is too
large, also user permissions should be granted only to protection
domains that have the necessary user principals and code signers, to
avoid injecting additional permissions into less privileged service
protection domains.
All data parsing the jvm performs, should also be moved into separate
modules, so that data parsing access controls and privileges could have
been managed (this is one of the missing checks you mention), and yes it
did provide a false sense of security for many years that Java
serialization was secure when it wasn't. I had many difficulties
explaining to developers in 2010 that Java serialization wasn't secure,
they didn't believe me and it cause problems.
Had the Java 2SE security infrastructure never been introduced, perhaps
something else would have evolved, or at the very least, our software
wouldn't depend on it. Java's access controls have certainly suffered
from a lack of investment.
Unfortunately our software is dependent on it and designed around it at
a fundamental level, even if SM is null (which incidentally I've haven't
yet tested, I think we have code that checks that SM is enabled), our
software is still using AccessControlContext's to establish TLS
connections and authenticate users. Personally I would like to see
parts of AccessController and AccessControlContext retained for
retrieving the Subject for establishing secure connections in a way
that's compatible across all Java versions.
After removing access controls, it effectively means AllPermission is
granted to every authenticated user (and in our case service). Any
access controls that we create at a higher level, can be circumvented by
the lack of lower level access controls. So the only access control is
authentication.
We have no desire or want to instrument the jvm, we have been advised
that this is the new way to implement access controls by OpenJDK, it's
simply that we want to continue to support future versions of Java and
cannot do so without access controls. Our software has been designed
with and depends on access controls.
Java has had access controls for 24 years, we couldn't foresee that it
would be removed in a breaking manner in such a short time frame.
Remember how long Thread.stop and similar bad api's remained in
deprecated form?
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8204243
This isn't something we wanted or planned to do, it's a task that has
been created for us and we have few resources to address it.
Regards,
Peter.
On 28/04/2022 3:37 am, Martin Balao wrote:
> David,
>
> I understand the reasons behind seeing authorization checks at the
> runtime layer as something that just adds, without any harm in the
> worst case (all of this putting the maintenance cost and other
> arguments aside.)
>
> My concern is more about the general security principles underpinning
> the idea. We will probably agree that half-barriers are not barriers,
> and might cause a false sense of security. If we have authorization
> checks at the runtime level, they must be comprehensive, coherent and
> well-maintained. Their availability suggests that mixing high-level
> checks with runtime-level ones can be part of a good security design
> in modern application development. For the reasons that we've been
> discussing, I'm not convinced of that. And even when subtle, I prefer
> the runtime not to make the suggestion. If you still want it, you can
> go ahead with instrumentation; but it's clear that for the runtime
> developers that is a workaround and not a desirable security design.
>
> What I mean by splitting responsibility is that application
> developers can use a mix of high and low level checks, at different
> layers, with more complexity. As Sean said, letting the unauthorized
> user to move towards the edge of the action is more risky. We can lose
> sight of workarounds and holes with the additional attack surface and
> complexity that comes at a lower layer. What I want to stress is the
> value of clarity, simplicity and division of responsibilities as a
> general security principle.
>
> Martin.-
>
More information about the security-dev
mailing list