On 20/06/2023 9:04 pm, Ron Pressler wrote:
On 20 Jun 2023, at 06:26, Peter Firmstone<peter.firmst...@zeus.net.au> wrote:
Don't get me wrong, it's good that OpenJDK is improving encapsulation, it's
just OpenJDK is also undoing years of tested and hardened API's,
You probably meant that as a bad thing, but I read it as thank you for serving
your users!
No, not at all, you're welcome. I recognise there are different levels
of trust, for different applications, you're bringing some of the
benefits a well deployed SM provides, without the deployment cost to
people who aren't using it now and in doing so improving their
security. Although we don't agree on everything, there are things we
agree on, and just because we disagree on some things doesn't mean I
don't respect your opinions. I realise that SM will be removed, I'm
just disappointed that we are being prevented from reimplementing a
replacement authorization layer in Java, without any compromise from
OpenJDK it's not possible. We at least need to retain some kind of
privilege action mechanism. That doesn't even have to be a privileged
action, maybe it could be an annotation, that we can find using StackWalker.
Authorization isn't a one size fits all, it's for high value assets,
usually determined by risk assessment, Java was once promoted as a
secure language for enterprise.
If data protected isn't of high value, or is of low complexity, a
solution of appropriate cost will be applied, in many cases that isn't
SM, however removing SM will reduce security below an acceptable level
for a percentage of developers and software projects, the number may be
small, but the impact will be widespread, just look at how widely
deployed ElasticSearch and OpenSearch are. JGDMS only gets about 30
unique cloners per fortnight, and about 50 clones, but sometimes its up
around 200, I don't know how widely used it is. But we aren't the only
projects using it for where there is no replacement possible. There are
many parts of the Java API, which aren't used by all developers. So
far, even though I still use some of the other API's which have been
removed from OpenJDK, it's been pretty straightforward to replace them
with something else. SM (JAAS Authorization) isn't replaceable.
I've been using and SM for 20 years, it's a lot simpler than people
realise, there is a way to use it, that makes it easy, and there are
ways not to, that are guaranteed to cause pain.
Arguments against SM, that I've read on this list are based on bad
practises, or the lack of tooling provided with the default
implementation, this indicates a lack of experience in use, I know that
Alan and Sean were involved during the early days with Li Gong, but I'm
pretty sure they don't have much experience deploying it, feel free to
prove me wrong if my assumption is incorrect. I'd suggest cost of
maintenance also appears overestimated, this is partly due to policy, I
have offered to assist with maintenance, however my offer was rejected,
as this is considered part of security, I am not permitted to assist, I
could submit patches to this list, but I'm not sure they'll be welcomed
or accepted.
When SM is used incorrectly, complexity is significantly increased,
however the API's could be simplified, which would improve security and
reduce complexity and maintenance.
SM no more impacts developers who don't use it, than developers who
don't use XML. The biggest maintenance burden I see on OpenJDK is Java
Serialization.
One dedicated full time resource should be sufficient, to review code
changes to check for compliance. It's not difficult for me to track
down where permissions are leaking from Java API's, however I've assumed
that as it's deprecated, there will be little interest in fixing them,
is my assumption incorrect? I think this looks like a big task,
because there is a lack of tooling. SpotBugs also provides static
analysis to find these problems, although now that SM is deprecated,
users are requesting it to be removed from SpotBugs.
Even an imperfect authorization layer is far better than no
authorization layer at all.
Lets discuss how it should be used, and what benefits it provides and
the situations when you might want to use it. I only ask that people
keep an open mind and for the moment, forget all the horror stories. I
realise this is academic now, and unlikely to change anything, however
documenting it here might serve some historical purpose.
Firstly, if used correctly, using authorization is no more difficult
than using an Executor framework.
Some ground rules:
1. Users create data. The level of trust you have for the user
determines the level of trust you have for their data.
2. Developers create code. The level of trust you have for the
developers determines the level of trust you have for their code.
3. Always, always, use automated tooling for the creation and
management of policy files, start with an empty policy file with no
permissions.
4. Apply the principle of least privilege.
5. Developers, users and administrators will make mistakes, no one is
perfect, mistakes can be fixed once identified, try to find ways to
identify mistakes, for example, check generated policy files for
leaking privileges, this indicates that a developer has forgotten to
use a PrivilegedAction.
6. Attempts at sandboxing have always failed historically, with
increasing language complexity, the more likely a sandbox will be
broken through, never run untrusted code, avoid parsing data from
unauthenticated sources. For example, code can perform DOS and use
up all the memory on your system to bring it down. Pictures can
contain code, which can be compiled and run, data can contain SQL
injection attacks. Always validate input, eg use pattern matching
to sanitize text.
7. Some architectures are more secure than others, for example, OpenBSD
on Sparc or ARM is not susceptible to Spectre, Meltdown or similar
attacks.
8. SM is not a sandbox, it is an authorization layer only.
There are different levels of trust, for different users and different
developers. Note that historically, Sun or the JRE had unlimited trust
for its own developers, this was a mistake. 100% Trusted code should
be minimised to bootstrap code.
Trust is from 0% to 100%. 0% is an unauthenticated user
(unauthenticated unknown data source), while 100% is your administrator,
there are many levels of trust in between. Applied to developers, 0% is
the unknown software your user downloaded from the internet, 100%
shouldn't be given to developers, only bootstrap code. I don't trust
myself 100% either.
General guidance:
1. If you are not authenticating users, using data integrity
checksums, or encryption, then you are unlikely to benefit from SM.
SM only provides authorization, you cannot make authorization
decisions without first authenticating, you have to establish trust
exists, before you can determine an appropriate level of trust.
2. Privileges must be granted to both users and developers, for
software to function, using tooling to determine permissions required.
3. There is no such thing as 100% trusted developers, all developers
make mistakes. Java Serialization is my case in point, it bypassed
all protections and there was no DataPermission("deserialize") to
allow authorization decisions to be made based on the authenticated
user who supplied the data, in many cases, due to the 100% trust
given to Java de-serialization code, there were no adequate defences.
4. When the developer is given 100% trust (AllPermission, or a JRE
ProtectionDomain with a null codesource), then the user will also be
trusted 100%, when this code parses the user data, this is why so
many gadget attacks were successful.
5. If 100% trust is given to developers, it adds unnecessary
programming complexity to reduce that trust for users, that's seldom
applied in practise.
6. As the developer, if you are writing code that parses data, you must
add permission checks before parsing, this allows the prevention of
parsing data from an untrusted user.
7. As the developer, if you are opening a network socket, accessing the
file system, key stores or properties, do so using a
PrivilegedAction. Keep the privileged action short and sweet, eg if
opening a socket, only do what's necessary to open the socket, and
return it.
8. PrivilegedAction's can be used with AccessController, and
AccessControlContext, to elevate, preserve or reduce privilege,
however it is most commonly used to elevate it.
9. Don't use the Policy provider supplied with OpenJDK, it relies on
DNS. Performance and scaling is horrible, with many synchronized
locks, network and file system access, sorry I don't intend to
offend, it was written a long time ago, it's basically broken.
10. Generate your policy files using a deployment environment, by
working the software through all of its use cases, for each role.
Properties should be defined, so the policy file generation tool can
replace file paths and network addresses in policy files with
configurable properties. The policy writing tool only appends
missing permissions, this is to allow policy files to be built up
incrementally, periodically, the entire policy file should be
regenerated, for example, following a software update.
11. Permissions in policy files will determine the users who can access
properties, files, network, or developers who use agents.
12. All external connections should require authentication. All
authenticated connections should be given a thread of execution with
the Subject of the authenticated user.
13. All code should either be signed, or be given a SHA256 hash or better.
14. Permissions can be used to control many Java features, such as the
attach api, reflection, code downloads, network connections, but has
no power to control Java serialization for example.
Example of a policy grant made by the policy writing tool, notice how
both the code and principal are required to have these permissions, that
means the user cannot accidentally grant these privileges to untrusted
code, and it means the code doesn't have permission if the user isn't
authenticated, or has the wrong credentials, the tooling works this out
for you, following the rules of the principle of least privilege, you
don't have to figure it out yourself, tooling significantly reduces
complexity and provides for embarrassment free, well organised deployment:
grant codebase "file:/${qa.home}/lib/jinitests.jar",
principal javax.security.auth.x500.X500Principal "CN=Tester"
{
permission net.jini.security.GrantPermission
"java.lang.reflect.ReflectPermission
\"newProxyInPackage.org.apache.river.mahalo\", \"\";
org.apache.river.api.io.DeSerializationPermission \"ATOMIC\";
java.lang.RuntimePermission \"accessClassInPackage.com.sun.proxy\",
\"\"; org.apache.river.api.io.DeSerializationPermission \"ATOMIC\";
java.lang.RuntimePermission \"accessClassInPackage.com.sun.proxy\", \"\";";
permission net.jini.security.GrantPermission
"net.jini.security.AuthenticationPermission
\"javax.security.auth.x500.X500Principal \\\"CN=Tester\\\" peer
javax.security.auth.x500.X500Principal \\\"CN=Mahalo\\\"\", \"connect\";";
permission java.net.URLPermission
"http://${HOST}:9080/mahalo-dl-3.1.1-SNAPSHOT.jar", "GET:";
permission net.jini.security.AuthenticationPermission
"javax.security.auth.x500.X500Principal \"CN=Tester\" peer
javax.security.auth.x500.X500Principal \"CN=Mahalo\"", "accept";
permission java.io.FilePermission
"${jsk.home}${/}lib-dl${/}${mahalo-dl.jar}", "read";
permission java.io.FilePermission
"${jsk.home}${/}lib${/}${mahalo.jar}", "read";
permission java.io.FilePermission
"${qa.home}${/}harness${/}configs${/}jsse${/}mahalo${/}mahalo.config",
"read";
permission java.io.FilePermission
"${qa.home}${/}harness${/}policy${/}defaultsecuremahalo.policy", "read";
permission java.io.FilePermission
"${qa.home}${/}harness${/}trust${/}mahalo.keystore", "read";
permission java.io.FilePermission
"${qa.home}${/}lib${/}qa1-mahalo-dl.jar", "read";
};
If we remove the ability of the Java platform to manage different levels
of trust, by removing authorization, then all trust becomes 100%, for
users who authenticate and developers whose code you run, you grant them
100% trust, to access anything that might be available from within the
JVM. So now you have to set file permissions and firewall permissions
to prevent the application from doing unauthorized things.
I would also go as far to say that no permissions is the right approach
for loom, one perform a privileged action in a Thread, maybe using a
task in a thread pool, then hand it off to loom, the same goes for
ForkJoin thread pools.
Our duty is to maximise the utility of the JDK. The reality of the economics of
software — in this case, the finite resources available to us — demands that we
work on features with a relatively high ratio of utility to cost and that we do
not work on features with a relatively low ratio of utility to cost. And by
“utility” I mean economic utility, i.e. the total value to the ecosystem. If
someone invented a galactic teleporter that no one uses, even for silly reasons
— say, they don’t like its colour — its economic utility is zero.
Let me put it bluntly and only somewhat inaccurately: Every feature added to
Java over the past few years is one that we believe will serve at least a
million Java developers (directly or indirectly); in fact, most features added
to Java have had a million users in other languages before even being
considered for addition to Java based on their technical merits (of course, the
calculus changes depending on cost).
that we're expected to come up with DIY solutions for, with zero care from
OpenJDK and some very bad examples of alternative solutions,
The alternative solutions are the ones preferred by the vast majority of users
who, I would assume, think they are superior solutions. We are simply unable to
also provide a separate solution, let alone such an expensive one, for a very
small minority. It would be unfair to most of our users.
not to mention a completely wrong assessment of SM based on an outdated poorly
maintained implementation, with lack of use and maintenance burden (when the
community is willing but not allowed to maintain it) as justification for it's
removal.
The community has not shown any willingness to maintain SM. As with other
features, we only ever keep something to be maintained by others if they show
both the commitment and the requisite ability (e.g. the JDK ports and GCs that
are not maintained by Oracle). There are other companies beside Oracle that
regularly contribute substantial, excellent work to OpenJDK, but to date none
of them do so at the scale required for this, and we’re talking companies that
pay several developers to work full time on OpenJDK. And that’s not even the
worst of it. One of the things that makes SM so costly is that it’s not a
separable feature but a cross-cutting concern that every JDK developer working
primarily in the core-libraries area needs to worry about it when working on
many changes in many areas.
I don’t think you’ve begun to appreciate the enormity of this task, but even
you complained that injecting doPrivileged calls in keeping up with all the
changes to the JDK is too much work and that’s before studying the test suite
that tests all of these places.
SecurityManager is just an authorization layer (that allows assigning different
levels of trust), not a magic wand designed to solve all security problems (or
sandbox, it was only a component in the sandbox, along with bytecode
verification and ClassLoader visibility), to be discarded because it doesn't
solve some other unrelated security flaw, it's an Non Functional Requirement,
it doesn't bring in any revenue for Oracle, nor does it for many other
development projects, so management have knocked it on the head, we get it.
You don’t get it, because it wasn't management. The technical leadership
decided to remove SM on the advice of our security experts because they’ve
judged that it’s harming our users by demanding too much effort for something
that too few people want to use. If management gave us fifty more developers
tomorrow to employ as we please, we still wouldn’t be able to maintain SM
because our responsibility to our users would still demand that we use those
resources for things that would give our users more utility.
The message I got, was it was a maintenance expense OpenJDK doesn't
want, that indicates a commercial decision. I have seen arguments
against it documented on JEP411, but these only apply to the
implementation of the Policy provider supplied with OpenJDK and lack of
tooling for policy file creation. These arguments are in error, and
I've demonstrated that previously, with fully functional high
performance high scaling code.
The problem is not that SecurityManager is being removed, the problem is
we have come to depend on using Authorization in Java, and now we are
being prevented from reimplementing anything to replace it.
We can handle the stack walk, using the attach api to insert guards, but
without privileged calls in OpenJDK, permissions cannot be applied using
the principle of least privilege, it will be broken and not useful.
Basically all authorization support foundations are being removed along
with SecurityManager, there's nothing we can do to fix it. Java's no
longer a platform that supports authorization.
SM was just the implementation, maybe it wasn't right, and maybe the
experts are right to remove it. But maybe they're wrong too.
Even the most security-conscious of our users have shown a clear preference for
other solutions. Your argument is that you know better than the market, but one
of the world’s most popular mainstream languages cannot be led in this way.
Perhaps you believe that better marketing would make the market see the light,
but we have a hard enough time educating our users about features that a
million others already enjoy in other languages, and an even harder time
educating them about more unique features (such as strong encapsulation) that
in a few short years have already paid more dividends than SM. Imagine how hard
it would be to convince them of an approach that’s been abandoned after a
couple of decades by the users of *both* popular platforms that adopted it.
We know that Oracle intends to remove authorization from the Java platform, we
also know that there is nothing we can say to convince Oracle it's a bad idea,
the only thing management will understand is bad consequences, such as bad news
headlines. I understand that Oracle needs to monetize Java
If you mean that a small group of users is unable to convince us to do what
they wish at the expense of most of our users, then you’re right. You say
“monetize Java”, but what that really means is that you understand — without
acknowledging — that most people don’t find your solution to be superior and
don’t want it. I fully sympathise with the frustration when the wider public
doesn’t share our views as I frequently experience it myself, but you seem to
blaming us for acting rationally and accepting the reality of the market we
serve.
the only thing management will understand is bad consequences, such as bad news
headlines.
I think what they’ll understand is actual demand. Get some real traction then
we can talk again.
We've been working hard at implementing authorization for many years, to
protect against attacks, but suddenly the threat is gone? Yeah right, there's
a bridge in Brooklyn for sale.
That is untrue, and you speak as if it’s 1996, not 2023. The threat is not
gone, but the vast majority of those writing secure servers prefer other
solutions to address such threats, in particular those provided at the OS
level. That approach has pros and cons and we could argue on the intellectual
merits of both approaches all day, but no one can deny that the economic
utility of the popular approach is much higher than the one you like. Maybe
you’re right and you know better than everyone else, but in that case it’s not
us that you need to convince; you need to convince the market.
Most of the market has low risk, low consequences for security breaches,
that's why security is often an afterthought, or non functional
requirement. It's the low probability, high consequence outcomes that
need protection, and these are a much smaller part of the market.
Still it will be damaging to Java's reputation when one of the high
consequence outcomes is realised. There's going to be a lot of legacy
code that isn't taking advantage of module encapsulation, something will
go bang at some point.
BTW, we’re not removing authorisation from the Java platform. Most Java
applications have authorisation and virtually none of them use SM. What you
mean is removing independent checks from low level operations inside the JDK.
Yes, JEP 411 is removing JAAS authorization from the Java platform.
Here's a refresher if you need it:
https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/AcnAndAzn.html
The decision has been made, it's final, the debate is over, we're just waiting
for the removal notice now. How about someone raise a JEP to remove security
manager, targeting a particular release version (preferably 22, but so be it if
it's 21) so we can remove uncertainty and notify our users of the versions of
Java we'll be supporting going forward? Then it's over and we can stop
worrying about it.
We will (try to) follow the same process we follow for all changes, including
more impactful ones. We don’t yet know what changes will be in JDK 22, let
alone later releases. When we do, so will everyone else. I hope we’ll find ways
to improve our process in the future so that it will inconvenience you less.
What will help will be a predictable roadmap, so we can do some planning
around that.
Thank you for your time, we both know SM is being removed, lets not
waste any more of each others time discussing it, we just need to know
which versions of Java we'll be unable to support and a timeline around
that. Then that opens the door to assessing which language platform is
best suited to our needs going forward.
Thank you, respectfully,
Peter.
— Ron