Safeguard
Industry Analysis

Java Security Explained

Java security failures like Log4Shell exposed 3 billion devices — here's why Java's dependency depth makes it uniquely risky, and how to fix it fast.

James
Principal Security Architect
Updated 8 min read

Java runs the backend of most Fortune 500 companies, and that ubiquity is exactly why it's the most exploited language ecosystem on the planet. When Log4Shell (CVE-2021-44228) surfaced on December 9, 2021, it scored a maximum CVSS of 10.0 and put an estimated 3 billion devices at risk — not because Log4j was obscure, but because it was buried three, four, or five layers deep in dependency trees that almost nobody had actually mapped. It remains the clearest case study in why java security flaws tend to hide many layers deep rather than sitting in a team's own code. Four months later, Spring4Shell (CVE-2022-22965) did it again. Java security isn't a niche concern for legacy shops; it's a supply chain problem hiding inside the JAR files, Maven repositories, and transitive dependencies that make up nearly every enterprise application still running in production today. This post breaks down where the real risk lives, walks through the incidents that prove it, and covers what actually reduces exposure versus what just generates noise.

Why is Java Still One of the Riskiest Languages to Secure?

Java is one of the riskiest languages to secure because its dependency model buries vulnerable code several layers deep, where standard code review never looks. Most java security flaws surface not in code a team wrote, but in a transitive dependency nobody remembers adding. A typical Spring Boot application declares 15-30 direct dependencies in its pom.xml or build.gradle, but Maven's transitive resolution pulls in 150-300 total artifacts once you count what those dependencies depend on. Making matters harder, the java security manager deprecated by JEP 411 in JDK 17 (and disabled by default in JDK 24) removed one of the platform's original in-JVM sandboxing mechanisms, pushing even more of the runtime security burden onto dependency hygiene and reachability analysis rather than enforcement baked into the runtime itself. Log4j itself was almost never a direct dependency — it arrived through logging shims like SLF4J bridges or was bundled inside application servers such as Apache Tomcat and Elasticsearch. Veracode's State of Software Security research has repeatedly found that the large majority of Java applications carry at least one known vulnerability in a third-party library, and Java consistently ranks among the slowest ecosystems to remediate flaws, with median fix times often exceeding a year for lower-severity findings that never get prioritized. The scale of Maven Central compounds this: it now hosts more than 600,000 unique artifacts, and a single mvn install can pull in code from dozens of maintainers a security team has never vetted.

What Made Log4Shell the Worst Java CVE in a Decade?

Log4Shell was the worst Java CVE in a decade because it combined trivial exploitability with near-universal deployment. The flaw lived in Log4j's JNDI lookup feature: an attacker could send a single crafted string — as simple as a User-Agent header or a chat message — that got logged and triggered a remote class load from an attacker-controlled LDAP server, resulting in full remote code execution with no authentication required. Apache disclosed it on December 9, 2021, affecting Log4j versions 2.0-beta9 through 2.14.1, and within four days security teams were tracking active mass scanning and exploitation against Minecraft servers, iCloud, Steam, and enterprise VMware, Cisco, and IBM products. The CVE hit a 10.0 CVSS score, and Apache had to ship three follow-up patches (2.15.0, 2.16.0, and eventually 2.17.1) after each initial fix left partial bypasses. The core lesson wasn't about Log4j's code quality — it was that most organizations couldn't answer "are we affected?" for days or weeks, because they had no accurate inventory of where Log4j lived across their fleet.

How Common Are Malicious Packages in the Java Ecosystem?

Malicious and typosquatted packages are a growing and measurable problem in Java's package ecosystem, not a theoretical one. Sonatype's annual State of the Software Supply Chain research has documented tens of thousands of malicious open source packages published across ecosystems each year, with a sustained year-over-year increase, and Maven Central has had confirmed incidents of credential-stealing and cryptomining payloads slipped into artifacts with names deliberately close to popular libraries. Because Maven's default resolution favors whichever version satisfies the dependency graph rather than flagging unusual publish activity, a newly published malicious artifact can enter a build within hours of release if a project uses loose version ranges. Text4Shell (CVE-2022-42889), disclosed in September 2022 in Apache Commons Text, is a reminder that this risk isn't limited to intentionally malicious packages — legitimate, widely-used libraries with tens of millions of downloads continue to ship exploitable deserialization and injection flaws that ride along silently until someone maps where they're actually called.

Does Every Vulnerable Java Dependency Actually Need a Patch?

No — most flagged vulnerabilities in Java dependencies are never reachable from your application's actual code paths, which is why patch-everything strategies burn out security teams. A generic java security scanner flags a vulnerability the moment a vulnerable version is present in the dependency tree, regardless of whether the vulnerable method is ever invoked — which is exactly the noise problem a 2023 analysis from the Endor Labs research team quantified, finding that roughly 60-70% of vulnerabilities identified by traditional software composition analysis (SCA) tools in open source dependencies are in code that is never called by the application. For Java specifically, this matters more than in most languages because of how deep transitive chains run — a CVE in a logging utility's XML parser might be flagged across 40 services, but only 3 of them ever call the vulnerable method. Without call-graph-level reachability analysis, security teams end up triaging hundreds of "critical" findings a month when the actual exploitable surface might be a dozen.

What's the Fastest Way to Fix a Vulnerable Java Dependency Without Breaking the Build?

The fastest safe fix is an automated, tested upgrade PR that bumps only the minimum version needed to clear the CVE, rather than a blanket "upgrade to latest" that risks breaking API compatibility. Java's ecosystem is notorious for major-version breaking changes — the Spring Framework 5-to-6 jump required a full Java 17 baseline, and Log4j 1.x to 2.x was a near-total API rewrite that many teams still haven't completed a decade later, which is part of why unsupported Log4j 1.x instances were still being found in the wild years after Log4Shell. This same drag applies to the runtime itself: plenty of production fleets are still running Java 8, and java 8 security vulnerabilities patched years ago in newer LTS releases remain exploitable wherever a team never upgraded the JVM, not just the application's own dependencies. Effective remediation means resolving to the lowest patched version that satisfies the fix, running the existing test suite against the proposed change, and opening a pull request with the diff pre-validated — not filing a ticket that sits in a backlog for eleven months, which is roughly the median time Veracode has found it takes organizations to close out a known third-party Java vulnerability.

How Safeguard Helps

Safeguard closes the gap between "this Java dependency has a CVE" and "this CVE is actually exploitable in your code" by running reachability analysis across your dependency graph to determine whether the vulnerable method is ever invoked by your application, cutting through the noise that makes traditional Java SCA scans unusable at scale. Unlike a generic java security scanner that stops at CVE matching, Safeguard also tracks which of your services are still running legacy runtimes — flagging java 8 security vulnerabilities and other unpatched-JVM risk alongside application-level dependency findings — and with the java security manager deprecated and eventually disabled entirely, reachability analysis is one of the few remaining ways to know whether a vulnerable class path can actually execute in your application. Griffin, Safeguard's AI-driven remediation engine, reads the vulnerable code path alongside your call graph to prioritize what's genuinely exploitable and to draft context-aware auto-fix pull requests — version bumps validated against your existing tests, not blind latest-version jumps that risk breaking Spring or Maven builds. Safeguard also generates and ingests SBOMs (CycloneDX and SPDX) directly from your build artifacts, so you get an accurate, continuously updated inventory of every JAR, transitive dependency, and Maven Central artifact in your environment — the exact visibility gap that turned Log4Shell into a weeks-long fire drill for so many teams. The result is a Java security program that ships fixes engineers will actually merge, instead of a backlog of CVEs nobody has time to triage.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.