Recently, I was in discussion with a Java user in the finance industry about the possibilities of using Azul Platform Core to run a range of applications. One of the most significant advantages for mission-critical enterprise applications is knowing that you have access to the latest security patches and bug fixes. With Platform Core, these are provided within a defined SLA after the embargo for updates is lifted (this is essentially the point in time when Oracle release the update to their JDK).
Security is a very serious concern when sensitive data is in use, and potentially huge sums of money could be stolen. I was, therefore, somewhat taken aback when the user said, “We’re not worried about installing Java updates as our core banking services are behind a firewall.”
I will be the first to admit that I’m not an expert on computer security, but this attitude really surprised me. The way to achieve the highest level of protection is through a multi-layered approach and ensuring that any potential vulnerabilities are addressed as soon as patches are available.
This led me to do some research in this area to provide them with a convincing argument as to why they should be updating their Java runtimes.
Why You Must Keep Java Updated
The first thing to understand is that firewalls do very little to protect networks. Firewalls operate in a reactive mode and can only limit packets being sent to machines within them. It is certainly possible to use the firewall to prevent access to specific ports, throw away malformed packets, reject packets that are too small or too big and so on. The real danger does not come from packets that aren’t allowed through but from the ones that are.
And the even bigger issue is that ever since people started developing software, others have been trying to utilize it in ways not originally intended. This is often for malicious purposes such as stealing credit card numbers or user’s identities and we use the term hacker to describe someone who is able to subvert computer security. There are a myriad of ways that hackers use to achieve their goals; from fooling people into revealing confidential information (phishing) to sophisticated manipulation of software through techniques like buffer overruns.
Unfortunately, the more complex a piece of software, the more likely there is to be a vulnerability that hackers can exploit. The JDK is a very complex piece of software and, as such, may contain vulnerabilities.
Overview of Java Vulnerabilities
For some additional background, it is helpful to understand how vulnerabilities are described and classified.
Security vulnerabilities for pretty much any software package or platform are reported as Common Vulnerabilities and Exposures (CVEs). MITRE, a not-for-profit organisation, maintains the CVE list of publicly disclosed cybersecurity vulnerabilities that is free to search and use. For each CVE, which is a description of the vulnerability, there is a corresponding Common Vulnerability Scoring System (CVSS) value that ranges from 0.0 to 10.0. The CVSS is determined from several metrics categorised as base, temporal or environmental. These metrics provide a breakdown of things like the attack complexity, whether privileges are required to use it, whether it can be exploited remotely across a network and so on. The CVSS values are then categorised as:
- Low: 0.1-3.9
- Medium: 4.0-6.9
- High: 7.0-8.9
- Critical: 9.0-10.0
A great example of why software should be updated with security patches is the hack carried out on Equifax, a consumer credit reporting agency. Equifax had numerous firewalls in place that would have done all the things a firewall should, but these did not prevent the attack. The initial breach of the network was through the Credit Dispute website, which was legitimately accessible through the firewall; this used the popular Apache Struts framework (which is written in Java) to generate dynamic web pages.
In March 2017, a vulnerability in Apache Struts was identified, and a patch to address it was developed. Apache published this with advice to update machines as quickly as possible. The vulnerability, CVE-2017-5638, had a CVSS of 10.0, meaning it was the most critical possible and could be easily exploited. Unfortunately, Equifax did not update their Struts systems, and hackers were able to identify these machines as vulnerable. The exploit used a specific Content-Type value in the HTTP header that made it possible to execute system commands on the server, a classic remote-command execution attack. This was used as an entry point to the Equifax network, allowing hackers to access 143 million user’s financial records and dozens of sensitive databases. They were also able to create more than 30 entry points into Equifax’s computer systems.
During the two months before the hack was discovered, the firewall was working perfectly. Remember, there was nothing wrong with the structure or size of the packets being passed to Struts; they were entirely valid for the firewall.
You might be forgiven for thinking that since Java is now 26 years old and one of the most ubiquitous computer platforms on the planet, that all possible security vulnerabilities have been identified and fixed. However, if we look at the JDK, we find that it consists of over 3.7 million lines of code. (I got this number by running cloc on JDK 16). With so much code, it is literally impossible to categorically state that there are no vulnerabilities. Java isn’t insecure; it’s really powerful and complex.
CVEs Addressed in Java Updates
Let’s look at how important it is to keep your JDK up to date for the most stable and secure version of Java. Using the information provided in the release notes for each Java update, I gathered some statistics on CVEs that have been addressed in the JDK.
First, here is a list of the highest-scoring CVSS in updates since July 2017. This was when Oracle stopped providing free public updates to either JDK 8 or JDK 11. (Technically, free updates are still available for Oracle JDK 8 but only for personal or development use. Commercial use outside of approved Oracle applications or the Oracle Cloud requires the purchase of a Java SE Subscription).
|7, 8, 11, 13, 15, 16.
|7, 8, 11, 14
|7, 8, 11, 13, 14
|7, 8, 11, 13
|7, 8, 11, 13
|7, 8, 11, 12
|6, 7, 8, 11
|6, 7, 8, 10
|6, 7, 8, 9, 10
|6, 7, 8, 9
|6, 7, 8, 9
As you can see, recently, things have been relatively quiet more recently with only medium-level vulnerabilities being addressed, but there have been some high and a few critical over the years.
I also looked at the total number of vulnerabilities addressed since January 2015 and broke it down by CVSS level:
- Low: 64
- Medium: 150
- High: 56
- Critical: 59 (of which 24 were 10.0)
That gives a total of 329 vulnerabilities addressed in the JDK over the last six years. The number of critical CVEs is 59, which should make you think about the importance of keeping your JDK up to date to maintain the highest levels of security for your applications.
How to Update Java to Maintain Security
Oracle provides two different versions of each update; this is the same for all of their software. These are called Critical Patch Updates (CPUs) and Patch Set Updates (PSUs).
The CPU contains only the changes that relate to addressing security vulnerabilities. The PSU provides all those changes, plus any other bug fixes, minor enhancements, etc. The reason for doing this is to enable a quick rollout of an update if critical vulnerabilities need to be patched. Since the CPU contains a smaller set of changes, it is less likely to have an impact on the stability of your application (i.e. less likely to stop your application from working). Having done touch-testing with the CPU, you can deploy it to ensure maximum security for your applications. You can then spend longer testing the PSU (typically running a full set of regression tests) before deploying that to production machines. This ensures maximum stability for your applications.
An important note here is that not all OpenJDK binary providers understand the distinction between a CPU and PSU. Some call their update a CPU when, in fact, it is the PSU. You should look closely at what you are getting before deploying.
To highlight the benefit of having both a CPU and PSU, we only need to look at the July 2020 update. This included a fix for a bug that, in itself, introduced a regression. The impact of this regression was that heavily used software, like Hadoop Cluster, Solr and Lucene no longer worked reliably, which is a serious issue for mission-critical applications using them. The regression was not in a security patch and therefore not included in the CPU.
The regression was resolved by a fix to the fix, issued (for JDK 8) as update 265, on July 27, which was 12 days after the release of the scheduled July update. The initial update contained a fix to a CVE with a base score of 8.3, details of which were made public in the release notes.
Had you been using one of the affected software systems, hackers would have had nearly two weeks to try to exploit this if you only had the all-inclusive PSU.
If you also had access to the CPU, you could have rolled out the necessary security patches, secured your systems against the threat and waited, free from worry, for the revised PSU to be released.
Keeping Your Java Secure
Azul’s Platform Core builds of the OpenJDK are targeted at users who want to ensure their systems deliver both the maximum level of security and stability. In addition to providing both the CPU (security only) and PSU (full) versions of each update, we endeavor to deliver those updates as quickly as possible after Oracle releases their version. Since the end of free public updates to JDK 8, Azul Platform Core customers have been able to download the update within one hour of the Oracle release. This is, essentially, simultaneous.
To make the process of keeping your Java secure and up to date as easy as possible, Azul Platform Core provides fully supported builds of JDK 6, 7, 8, 11, 13, 15, 17, and 18 with all security patches backported where applicable.
Knowing that Java has vulnerabilities, no matter the version, it’s time to do an audit of your runtimes.