Safeguard
AppSec

Java Code Security: A Practical Checklist

Java code security has a specific set of recurring failure modes — deserialization, XXE, dependency sprawl — this checklist covers the ones worth checking on every review.

Safeguard Research Team
Research
5 min read

Java code security has a smaller, more predictable set of recurring failure modes than the language's size might suggest — insecure deserialization, XML external entity injection, dependency vulnerabilities inherited through Maven or Gradle, and a handful of XSS patterns specific to how Java web frameworks render output. A checklist built around these categories catches most of what shows up in real Java codebases, and it's a useful complement to whatever a java security scanner flags automatically.

Why does deserialization show up so often in java code security discussions?

Java's built-in serialization mechanism (ObjectInputStream.readObject()) reconstructs a full object graph from a byte stream, and if that stream comes from an untrusted source, an attacker who can find a "gadget chain" — a sequence of classes on the classpath whose ordinary methods chain together into unintended behavior — can achieve remote code execution purely by having the application deserialize their input. This isn't theoretical: libraries like Apache Commons Collections have had widely exploited gadget chains, and the ysoserial tool exists specifically to generate working payloads against common gadget chains found in typical Java dependency trees. The practical fix is avoiding native Java serialization for anything touching untrusted input entirely — use JSON or another data-only format — and if you can't, restrict deserialization to an explicit allowlist of expected classes.

What should a Java XML review check for?

Java's default XML parsers (DocumentBuilderFactory, SAXParserFactory, XMLInputFactory) resolve external entities and DTDs unless explicitly disabled, which is what makes XXE injection possible: a crafted XML document with an external entity reference can read local files or reach internal network services through the parser itself. The fix is a few lines of factory configuration — disabling DOCTYPE declarations and external entity resolution — but it has to be applied to every parser instantiation in the codebase, and it's easy to miss when parsing happens in a third-party library rather than application code directly. Any code review touching XML input handling should confirm these settings are actually in place, not just referenced in a coding standard document somewhere.

How does java xss differ from XSS in other server-side languages?

Java XSS follows the same root cause as XSS anywhere — unescaped user input rendered into an HTML, JavaScript, or attribute context — but the specific risk surface in Java depends heavily on the templating layer: JSP with raw EL expressions, Thymeleaf, or a JSON API feeding a separate frontend. Older JSP code that uses <%= %> scriptlets to output request parameters directly is the classic vulnerable pattern; modern frameworks like Thymeleaf auto-escape by default in most contexts, which has reduced but not eliminated the risk, particularly around th:utext or any construct that explicitly opts out of escaping. A java security scanner focused on data-flow analysis will trace user input to these unescaped output sinks specifically, which is more reliable than a manual grep for raw output tags.

What does dependency risk look like specifically in a Java project?

Maven and Gradle both resolve transitive dependencies automatically, which means a project's actual dependency tree is often five to ten times larger than what's declared directly in the pom.xml or build.gradle — and a known-vulnerable library several levels deep is just as exploitable as one declared at the top level. Log4Shell is the reference case here: a logging library nearly every Java application depended on transitively, with a vulnerability severe enough (unauthenticated remote code execution via a crafted log message) that dependency depth alone doesn't reduce real risk. Safeguard's SCA product resolves the full transitive tree for Maven and Gradle projects and flags known vulnerabilities regardless of how deep the vulnerable library sits, which matters specifically because manual dependency review rarely goes past the first or second level.

How should Java code security fit into a normal development workflow?

Static analysis in the IDE and at pull-request time catches the injection and deserialization patterns above before merge; dependency scanning on every build catches newly disclosed CVEs in existing dependencies even when no code changed. Neither replaces manual secure code review for business-logic authorization bugs, but together they cover the mechanical, repeatable failure modes well enough that a review can focus on the parts a scanner genuinely can't reason about. Pairing SAST/DAST with SCA gives a single view across first-party code and dependency risk instead of two separate tools with no shared triage queue.

FAQ

What's the single highest-priority item on a java code security checklist?

Dependency scanning, in practice — the sheer number of transitive Java dependencies and the frequency of high-severity CVEs in widely used libraries (Log4Shell being the clearest example) makes unpatched dependencies the most common real-world entry point, ahead of first-party code bugs.

Is Java inherently less secure than other backend languages?

No — Java's memory safety and mature tooling ecosystem make several vulnerability classes (buffer overflows, for instance) largely non-issues compared to lower-level languages. Its risk profile is shaped more by dependency sprawl and legacy serialization patterns than by language-level weaknesses.

Does Spring Boot handle java code security concerns automatically?

Spring Boot and Spring Security handle a meaningful share of common concerns by default — CSRF protection, secure session handling, auto-escaping in Thymeleaf — but they don't protect against insecure deserialization if an application adds its own unsafe deserialization logic, or against dependency vulnerabilities in libraries the framework doesn't control.

What's the fastest way to check for XXE risk in an existing Java codebase?

Search for XML parser instantiation (DocumentBuilderFactory.newInstance(), SAXParserFactory.newInstance(), XMLInputFactory.newInstance()) and confirm each one explicitly disables external entity resolution and DOCTYPE processing — a java security scanner with data-flow rules for this pattern will find these faster and more completely than a manual search.

Never miss an update

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