Java – Integrity by Default (JVMLS 2024)
youtube.com
youtube.com
You have a very strange threat model given how stunningly trivial it is to decompile .class files
I'm with you on the JNI but I am not willing to "baby with the bathwater" the Reflection API just avoid having to check the bytecode for Runtime.loadLibrary
private java.security.PrivateKey _key;
$ java -jar cfr.jar ...
$ sed -i"" -e s/private/public/g $(find . -name "*.java")
$ javac ...
and now Reflection doesn't need setAccessible and your threat model of "but my field is private!!11" has gone poofThe point I'm making is that I don't understand for the life of me whose problem this egregiously disruptive change is solving outside of the NSA, who I'm sure have their own JVM builds and can leave the rest of us out of it
Integrity is also a prerequisite to writing any security mechanism in any layer of the code that is less prone to accidental vulnerabilities in a third-level dependency, and it's also a prerequisite to performance optimisations. For example, if Java strings were immutable, the compiler would be able to optimise some operations on them. Except it can't because even thought they're almost always immutable, some third-level dependency may decide to mutate one string in the program, and because of that the compiler can't optimise any.
Such new and powerful functionality is indeed disruptive (in a good way), and all it demands is that libraries that need to, say, mutate Java strings or final fields let the application know so it could add a flag (if it's willing to take the portability/performance/security risks involved). That's it.
I don't see how this is relevant. I'm talking at runtime. If I have a class:
class Point {
private final int x;
private final int y;
Point(int x, int y) {
if (x < 0) throw new IllegalArgumentException("x less than 0");
if (y < 0) throw new IllegalArgumentException("y less than 0");
this.x = x;
this.y = y;
}
}
You should not be able to use reflection to remove the private-ness of my field to set Y to -42, unless you are code within my module that I control.But security is just a small part of the problem. Remember all the breakages people had upgrading from 8 to 9+? Virtually all of them were do to libraries reaching into internals that then changed. If no internal method could change because someone might be relying on it then the platform can't evolve. If an application author cannot know which of her dependencies may be non-portable, they can't weigh the risk.
Yet another problem is optimisations. We can't optimise code if any dependency can freely decide to change the meaning of any line of code in the program.
But as a general rule: as a library author I deeply resent people who cause ME to get blamed when I change an implementation detail and their fragile code breaks when my library gets upgraded.
> This update broke my workflow! My control key is hard to reach, so I hold spacebar instead, and I configured Emacs to interpret a rapid temperature rise as "control".
The semantics of encapsulation are sufficient. Anyone who breaks them knows it's on them, and if they don't, they certainly should have. It's not worth sweating all the maybes and what-ifs.
I agree, the issue arises when library developers make this decision and application developers have to deal with the fall out of that. Application developers need to know when things like this happen.
I do see the "by default" in the title, but while I would be turbo sad I would also gladly accept -Djava.security.integrity=false to allow me to opt out of this DRM-esque stuff
And I say DRM but I could also imagine a world in which this stunt would be the death knell to Spring :-(
So, like I said: if you're a big HFT fund and you need your JVM to do crazy integrity assertions, good for you, but don't burn the whole world down for them. Hell, I would be Azul makes a killing catering to that audience, so let them sell an integrity enabled JVM and leave the rest of us out of this fight
> the big fat zero of times where someone has complained about an integrity violation on the JVM
~99% of the migration difficulties from 8 to 9+ were due to integrity violations of the core library (remember, module encapsulation was turned off by default until JDK 16). The cost of that alone is, well, high, and that doesn't even count library vulnerabilities. Integrity violations have been the #1 cause of complaints over the last several years.
Integrity is important because it is a prerequisite to portability ("upgradability"), security, correctness, and some important performance optimisations. If you haven't heard people complain a lot about any of these things then you haven't been listening.
That's as much an indictment of the Security Manager than anything else. ;-)
> Every one of these features were added because they were useful to java developers.
Which creates a requirement in my mind that removing those features needs to be justified by adding more benefit than they take out, but it is entirely possible that those features were the best way to address the needs of Java developers.
The reality is that Java is no longer seen has being a high integrity runtime anymore, so nobody uses it that way. The use cases for a high integrity runtime haven't gone away though.
Second, the point about SecurityManager is a subtle one. SecurityManager was a complex security mechanism; this isn't. However, because integrity is a prerequisite to robust security, SM had to allow configuring integrity, but it was extremely hard to configure correctly. Now it's on by default.
If you have any sort of authentication/authorisation mechanism in your application (nothing to do with SecurityManager), then an accidental mistake in any dependency or transitive dependency may result in a vulnerability that would allow a remote attacker to bypass your security mechanism, because the attack surface area is the entire program.
Finally, the lack of integrity meant that some powerful performance optimisations can't be performed. Even simple constant-folding of Strings or final fields cannot be done because any library could unilaterally choose to mutate a String or a final.
CDI, Spring, JPA, Mock Testing (Mockito) all break the rules in some manner, like creating instances of objects without calling constructors, changing final class definitions, etc.
CDI, JPA, Spring are all used "in production", while Mockito would be used only in testing.
I have no idea how you balance these two. sun.misc.Unsafe was pretty hard to use and any normal human knew to stay away from it.
Most of the time breaking the rules is a reflection of either a bad practice or lack of functionality in the language. I'm all for experimenting in either of those dimensions, but there's a lot of value to have a default behaviour of not doing that.
Yes actually, especially when it comes to i/o. Let's say I want to simulate how many code handles a REST interaction with a third party service. I want to be able to mock the REST callout without performing any actual i/o. This is why these test frameworks are powerful and useful.
Later if I want I can test to see if the actual callout works by hitting a local endpoint.
Why? Because it's hard for you to mock out actual I/O, which could be solved by a tool that makes it easy to mock out actual I/O.
Testing it as part of your test cases violates the "Test one thing and one thing exactly" principle. The goal is to reduce the surface area of the test as far as possible to distill what the author of the code wrote is correct.
In reality, you're actually testing a bunch of method invocations that won't happen when the code is being used, and to do it, you're also breaking a lot of the contracts that one would otherwise expect to be maintained... maybe that's not the optimal way to do things.
I would love to modularize our app but AFAIU Android doesn't care about that. It doesn't choke if it sees modules (anymore) but it does not enforce the access restrictions.
If a third-party library is not updated to not use require reflection, the speaker considers it "a red flag that it's improperly maintained" and suggests looking for alternatives. This is just plain ignorance: for particular domains, there are libraries that either don't have alternatives, or the cost of switching to the alternatives is too high. Are JVM developers going to pay me to write a replacement for every old library essential for my projects? This simplistic approach severely downplays the impact on what the speaker calls "old applications", i.e. the battle-tested, money-generating, useful applications.
Like, I get the usefulness, and I get that it's an attack surface, but it seems the author never had to deal with real-world projects where going into the internals of other dependencies to fix them or access hidden functionality is an absolutely crucial requirement that creates value for the project.
Java 1.8 is good enough for eternity, so they have to break it to keep getting a salary.
Just like Google/Apple have to break HTTP/1.1 to force adoption of their "obfuscated by complexity" protocols.
What needs to be fixed in Java is the Virtual Thread pinning, and that is probably the last usable feature they will ever be able to add.
They are also trying to deprecate AWT, for their non open-source JavaFX.
The only way to not remain a slave is to break free:
Compile your own Java!
Compile your own Linux!
Buy redundant hardware that does not break easily and is repairable.
Make your own devices if you can.
Make your own encryption.
I don't know, there are quality of life improvement that I quite like in more recent versions.
> They are also trying to deprecate AWT, for their non open-source JavaFX.
JavaFX is GPL with a classpath exception, what are you talking about?
> Make your own encryption.
Why? That's generally considered bad advice.
Java 8 is an antiquated runtime that under performs in all categories against other modern runtimes.
> What needs to be fixed in Java is the Virtual Thread pinning, and that is probably the last usable feature they will ever be able to add.
It is fixed. Go download the EA release.
> They are also trying to deprecate AWT, for their non open-source JavaFX.
FX is under the same license as OpenJDK (GPL) and available on Github.
Is there a non open-source JavaFX? The only version of JavaFX I know of is GPLv2