Security vulnerabilities in Java applications are rarely the result of a single catastrophic mistake. More often, they emerge from a pattern of small, seemingly harmless decisions: a missing input validation check, a hardcoded credential that slipped through, an XML parser left with its default configuration. A structured Java security code review is one of the most effective ways to catch these issues before they reach production.
This guide covers what a Java security code review involves, how to conduct one effectively, and a practical Java security code review checklist aligned with OWASP guidelines and the realities of modern enterprise Java development.
What Is a Java Security Code Review?
A Java security code review is a systematic examination of Java source code—and in some cases compiled bytecode—with the explicit goal of identifying security vulnerabilities. It differs from a standard code review in its focus: while a typical peer review checks for correctness, maintainability, and conformance to coding standards, a security code review asks specifically whether the code can be exploited by an attacker.
Security code review can be conducted through several approaches:
- Manual review: A developer or security engineer reads the code, tracing data flows from input sources to output sinks, looking for missing validation, insecure configurations, and dangerous API usage.
- Automated static analysis: Tools scan the code for known vulnerability patterns without executing it. These catch well-defined issues quickly at scale but miss complex business logic flaws.
- Hybrid review: Automated tools identify candidates for manual inspection; reviewers focus human attention on high-risk areas flagged by tooling. This is the most effective approach for large codebases.
Why Java Security Code Review Matters
Modern Java applications are complex systems. They depend on dozens of frameworks (Spring, Hibernate, Quarkus, Micronaut), communicate over REST and gRPC APIs, process untrusted user input, manage sensitive data, and run on shared cloud infrastructure. Each of these dimensions introduces potential security issues that only become visible by examining the code itself.
The cost of finding a security vulnerability in code review is dramatically lower than finding it after deployment. A NIST study frequently cited in the industry estimates that fixing a defect in production costs 100 times more than fixing it during development. For security vulnerabilities, where the cost of a breach includes regulatory penalties, reputational damage, and customer harm, the ratio is even higher.
Security code review is also increasingly a compliance requirement. PCI DSS, SOC 2, ISO 27001, HIPAA, and FedRAMP all include requirements for secure development practices that typically include code review as a control.
Java Security Code Review Checklist
The following Java security code review checklist covers the most critical categories aligned with the OWASP Top 10 and Java-specific risks.
1. Input Validation and Injection Prevention
- All user-supplied input is validated against an allowlist of expected values or patterns before use.
- No string concatenation is used to build SQL queries; all queries use PreparedStatement or JPA named parameters.
- LDAP queries use parameterized APIs and do not concatenate user input.
- OS command execution does not include unsanitized user input; prefer library alternatives to Runtime.exec().
- XML parsers are configured to disable DOCTYPE declarations and external entity resolution.
- JNDI lookups do not include user-controlled values (protection against Log4Shell-class vulnerabilities).
- HTML output is contextually encoded using a library such as OWASP Java HTML Sanitizer.
2. Authentication and Session Management
- Passwords are stored using bcrypt, scrypt, or Argon2—never MD5, SHA-1, or unsalted SHA-256.
- Session tokens are cryptographically random with sufficient entropy (at least 128 bits).
- Session IDs are rotated on privilege escalation (e.g., after successful login).
- Spring Security’s SessionFixationProtectionStrategy (or equivalent) is configured.
- Logout invalidates the session on the server side, not just on the client.
- Account lockout or rate limiting is applied to authentication endpoints.
3. Authorization and Access Control
- Every sensitive endpoint or method is protected by an explicit authorization check.
- @PreAuthorize annotations (or equivalent) include ownership validation, not just role checks.
- Direct object references in URLs (e.g., /invoices/{id}) are validated against the authenticated user’s permissions.
- Admin and privileged APIs are protected by separate, more restrictive access control policies.
- The application does not expose stack traces, internal paths, or implementation details to unauthenticated users.
4. Sensitive Data Exposure and Cryptography
- Secrets, API keys, and credentials are not hardcoded in source code or configuration files committed to version control.
- Data at rest is encrypted using AES-256 or equivalent; weak algorithms (DES, 3DES, RC4) are absent.
- TLS 1.2 or 1.3 is enforced for all network communication; SSLv3, TLS 1.0, and TLS 1.1 are disabled.
- SecureRandom is used for all cryptographic randomness; java.util.Random is not used for security-sensitive operations.
- PII and sensitive fields are masked or omitted from application logs.
5. Error Handling and Logging
- Empty catch blocks are absent; all exceptions are either handled or rethrown.
- Exception messages shown to users do not include stack traces, SQL error details, or file system paths.
- Authentication failures, authorization violations, and other security events are logged with sufficient detail for forensic analysis.
- Log injection is prevented by encoding user-supplied values before including them in log messages.
- Logs do not include passwords, session tokens, or full payment card numbers.
6. Deserialization
- Java native serialization (ObjectInputStream) is not used with untrusted data.
- If serialization is required, a deserialization filter (ObjectInputFilter, Java 9+) is configured to allow only expected classes.
- Third-party serialization libraries (Jackson, Gson, XStream) are configured securely: polymorphic type handling is restricted, default typing is disabled.
- Deserialization of external data uses schema-validated formats (JSON Schema, Protocol Buffers, Avro) rather than Java serialization.
7. Dependency and Supply Chain Security
- A software bill of materials (SBOM) is maintained for all direct and transitive dependencies.
- No dependencies with known high- or critical-severity CVEs are present in the build.
- Dependency versions are pinned or locked to prevent unexpected updates introducing vulnerabilities.
- Build tool configurations (pom.xml, build.gradle) do not fetch dependencies from untrusted or unverified repositories.
8. Concurrency and Thread Safety
- Shared mutable state accessed by multiple threads is properly synchronized or uses thread-safe data structures.
- Singleton beans in Spring (default scope) do not hold request-scoped state.
- Time-of-check to time-of-use (TOCTOU) race conditions are absent in file operations and permission checks.
- Static fields are not used to store user-session data or request-scoped context.
How to Conduct an Effective Java Security Code Review
Step 1: Understand the Application’s Attack Surface
Before reviewing a single line of code, map out the application’s attack surface: all entry points where untrusted data enters the system (HTTP endpoints, message queue consumers, file uploads, scheduled job inputs), all trust boundaries where privilege levels change, and all sensitive operations (authentication, authorization decisions, payment processing, PII access). This map focuses the review on the highest-risk areas.
Step 2: Trace Data Flows from Source to Sink
Security vulnerabilities most commonly arise at the points where untrusted data reaches a sensitive operation—a SQL query, a file path, an OS command, an HTML output. Security-focused code review systematically traces the path of each user-controlled value from its entry point (the source) through the application’s logic to wherever it is ultimately used (the sink). At each step, the reviewer asks: is this value validated? Is it encoded? Can it be manipulated to change the intended behavior?
Step 3: Review Authentication and Authorization Logic Carefully
Authentication and authorization flaws—broken access control is consistently the number-one category in the OWASP Top 10—are often difficult for automated tools to detect because they require understanding the intended business logic. Manual review of authorization checks is essential, with particular attention to edge cases: what happens if a parameter is null? What if a user accesses an endpoint in an unexpected order? What if a resource changes ownership mid-session?
Step 4: Inspect Framework and Library Configuration
Java security vulnerabilities frequently reside not in custom code but in the misconfiguration of frameworks and libraries. Spring Security configurations deserve careful review: are all endpoints explicitly protected, or is there an implicit permissive default? Are CSRF protections enabled for stateful web applications? Is the Jackson ObjectMapper configured to reject unknown types? Is CORS configured with the minimum required origins, not a wildcard?
Step 5: Run Automated Tools and Triage Results
After completing the manual review, run automated SAST tools to catch patterns that may have been missed. SpotBugs with FindSecBugs identifies over 130 security-specific bug patterns in Java bytecode. OWASP Dependency-Check identifies dependencies with known CVEs. Triage tool results carefully: false positives are common, and spending time on them reduces the value of the review.
Automating the Java Security Code Review Process
Static Application Security Testing (SAST)
SpotBugs with the FindSecBugs plugin is the most widely deployed open-source SAST tool for Java. It runs on compiled bytecode, covering injection flaws, insecure deserialization, weak cryptography, XXE, and Spring- and JSP-specific vulnerabilities. Commercial alternatives such as Checkmarx, Veracode, and Semgrep offer higher precision and are appropriate for organizations with mature AppSec programs.
Software Composition Analysis (SCA)
SCA tools analyze the dependency manifest and the compiled application to identify third-party components with known vulnerabilities. OWASP Dependency-Check is the leading open-source option. Snyk, Mend, and GitHub’s Dependabot integrate with pull request workflows to block merges that introduce vulnerable dependencies.
Security as Pull Request Gates
Configure SAST and SCA as required checks on pull requests, not advisory annotations. A critical or high-severity finding should block the merge until it is resolved or explicitly accepted with documented justification. This shifts security review left—catching issues before they reach main—and creates a clear audit trail of security decisions.
Conclusion: Making Java Security Code Review a Team Habit
A Java security code review is most effective when it is embedded in the team’s normal development workflow rather than treated as a one-time audit before a major release. Teams that review code for security at every pull request—using the checklist as a reference, automated tools as a first pass, and manual review for high-risk areas—build security awareness into the culture of development, not just the process.
How Azul Intelligence Cloud Extends Security Code Review to the Runtime Layer
A thorough Java security code review covers the code your team writes. But modern Java applications are built predominantly from third-party components—frameworks, libraries, and transitive dependencies that no one on the team wrote or reviewed. Azul Intelligence Cloud extends security review to that layer, providing runtime evidence about which jars, methods and classes are actually reachable in production.
From Static Checklists to Runtime Evidence
The dependency section of a Java security code review checklist—verifying that no known-vulnerable libraries are present—is one of the most time-consuming and false-positive-prone parts of the process. SCA tools routinely flag dozens of CVEs in transitive dependencies, the vast majority of which affect code paths that the application never executes.
Azul Intelligence Cloud operates at the class level inside the JVM, showing the classes actually loaded and executed in the running application . The result is runtime evidence since you can see whether a vulnerable class is being executed, or not
Continuous Security Review Between Formal Code Reviews
Code reviews happen at commit and pull request time. But a dependency that is clean today may develop a newly disclosed CVE tomorrow—without any code change. Azul Intelligence Cloud provides continuous monitoring between formal review cycles.
This continuous posture complements the point-in-time nature of code review: the review validates what was built, and Intelligence Cloud validates what is running. Together, they provide complete coverage of the application’s security posture from development through production.
Zero Performance Overhead in Production
Azul Intelligence Cloud collects class-level execution data without inflicting performance penalties on running applications. The instrumentation is integrated at the JVM level, making it invisible to application workloads while providing continuous security intelligence to the teams responsible for application safety. Intelligence Cloud also supports any JVM—not just Azul’s own distributions—making it applicable to heterogeneous Java environments.
Operationalizing the Security Code Review Checklist
For teams following the Java security code review checklist covered in this article, Azul Intelligence Cloud operationalizes the dependency and supply chain categories at runtime:
- Code inventory: Intelligence Cloud tracks which classes and code paths are actually executed, helping teams identify whether vulnerable jars, methods or classes are being run in production. It can also identify dead and unused code for removal, thereby reducing an application’s attack surface.
- Any-JVM support: Intelligence Cloud works with any JVM in your fleet, making it applicable to heterogeneous Java environments without requiring a runtime migration.