previously prevented exploit now possible with JDK 18
Sean Mullan
sean.mullan at oracle.com
Tue Nov 2 18:34:58 UTC 2021
Hello Rick,
It is behaving as expected. Let me explain in more detail.
First, loading a signed JAR off the classpath only verifies the
signature and digests of the JAR file. It does not validate the signer's
certificate chain or determine if the signer is trusted. The JarFile API
class description includes this warning [1]:
"Please note that the verification process does not include validating
the signer's certificate. A caller should inspect the return value of
JarEntry.getCodeSigners() to further determine if the signature can be
trusted."
Some frameworks such as Web Start have this additional checking
built-in. Or, if you run your code with a Security Manager, then
additional steps will be performed at run-time to check that the code is
signed and that the signer's public key is trusted before granting
permissions to that code.
If you don't perform these additional steps, then the JAR can be
modified without detection. For example, the signature related files
could be removed from the JAR (thus making it an unsigned JAR), or the
JAR could be modified and then re-signed with a different key. In either
of these cases, the JVM would load the JAR without any exception.
It is also possible that a JAR signed with a weak or broken algorithm
(such as MD5 or SHA-1) could be modified without detection.
This is why the JDK implementation supports several security
properties which are used to disable cryptographic algorithms and
protocols that are weak or broken. This provides out-of-the-box security
and is important to safeguard against crypto algorithms that inevitably
become weaker over time. One of these properties is
"jdk.jar.disabledAlgorithms". The specification of this property defines
the behavior if a signed JAR file is signed with an algorithm that is
disabled [2]:
"JARs signed with any of the disabled algorithms or key sizes will be
treated as unsigned."
The JDK determined after step 1 of the JAR verification process [3] that
the JAR was signed with SHA-1, and therefore stopped further processing.
You may ask why we don't throw an Exception in this case. Although this
was considered, the compatibility risk was too high. These
restrictions are nearly always backported to earlier JDK update
releases. Throwing an Exception would be too high of a risk for
applications that happen to load signed JARs off the classpath but don't
otherwise behave any differently. Our primary focus is to protect
applications that verify that the code is signed by someone they trust.
Consider updating and re-signing your signed jar with a stronger,
non-broken algorithm such as SHA-2. SHA-2 is the default digest
algorithm used by jarsigner when the -digestalg option is not specified.
I hope this information is useful. I do think this is an area where our
javadocs and guides could be improved to provide more information about
how signed JARs are verified including more details on the behavior of
the JDK implementation with respect to disabled algorithms. We will be
working to try to improve the docs for JDK 18.
--Sean
[1]
https://download.java.net/java/early_access/jdk18/docs/api/java.base/java/util/jar/JarFile.html
[2]
https://github.com/openjdk/jdk/blob/master/src/java.base/share/conf/security/java.security#L667
[3]
https://docs.oracle.com/en/java/javase/17/docs/specs/jar/jar.html#signed-jar-file
On 10/28/21 3:14 PM, Rick Hillegas wrote:
> As a canary in the mineshaft, I built and tested Apache Derby with the
> recent build 18-ea+20-1248 of Open JDK 18. I tripped across the
> following issue when running Derby's regression tests. The problem is
> explained in more detail at
> https://issues.apache.org/jira/browse/DERBY-7126, where a simple repro
> (DERBY_7126_A) can be found. The problem is almost surely the result of
> work done on https://bugs.openjdk.java.net/browse/JDK-8269039 (Disable
> SHA-1 Signed JARs).
>
> Under previous versions of the JDK, the JVM would raise an error if you
> tried to load a class from a jar file which had been signed with SHA-1
> but later hacked by inserting malware via "jar -uf". This was the error:
>
> SHA1 digest error for $corruptedJarFileName
>
> However, under JDK 18 the hacked class loads, no error is raised, and
> the malware can now be executed. I was surprised that a previously
> prevented exploit now works. I think it would be better if the JVM still
> refused to load the hacked class even though SHA-1 has been deprecated.
>
> Thanks,
> -Rick
>
More information about the security-dev
mailing list