Java Application Security: A Practical Review and Migration Guide - Yenra

Review trust boundaries, authorization, TLS, secrets and dependencies, and replace obsolete Security Manager assumptions with explicit controls.

A navy application pavilion sits behind layered glass walls with an ivory key and an amber inspection lens.
Conceptual illustration: effective application security combines several explicit controls and evidence that they work.

A Java security review starts with what the application accepts, which identities may act, what data it can reach and how those decisions are enforced. This guide turns those questions into a review you can record and repeat. It also explains the migration consequences of older Java sandbox assumptions.

Record the application and its trust boundaries

Inventory the deployed JDK vendor and exact build, application framework, dependencies, database identity, network exposure and secret sources. Follow one representative request from its untrusted input to its database or external-service access, then to its output. Identify where authentication establishes identity and where authorization decides what that identity may do.

Download the Java security review worksheet (Markdown)

Use the worksheet to attach evidence, an owner and a due date to gaps. Start with the Java 25 documentation baseline used by this guide, then adapt it to the actual deployed versions. The Java secure coding guidelines provide detailed guidance on limiting access, input handling and reducing attack surface.

On a narrow screen, scroll the table horizontally. Keyboard users can focus the table and use the arrow keys.

Boundary questions that lead to useful tests
BoundaryQuestionEvidence to collect
Incoming requestWhat size, shape and identity are accepted?Malformed, oversized and unauthorized request tests.
Database accessWhich rows may this user read or change?Cross-user access tests and database privilege review.
Outbound connectionWhich destinations can application input select?Destination allowlist, redirect handling and egress policy.
Rendered outputIn which context is untrusted data inserted?HTML-text, attribute, URL or script-specific handling.
Runtime environmentWhat can a compromised process reach?Service identity, filesystem permissions and network rules.

Run behavioral checks in an authorized staging environment with synthetic data. A recorded pass supports that particular case; keep the remaining boundaries visible rather than treating a checklist as a security certification.

Separate valid input from authorized access

Validate inputs at the boundary for both format and size. A numeric invoice ID can be perfectly valid while belonging to a different user. Bind database values through prepared statements, then apply an ownership or role rule at the service boundary before returning the result. The JDBC guide shows parameter binding and transaction handling.

At an output boundary, encode for the exact context. HTML text, HTML attributes, JavaScript strings and URLs have different rules. Prefer framework templates and established encoders for the context in use. OWASP's XSS prevention guidance describes how untrusted data can become executable browser content. The JSP example demonstrates escaped HTML text; it is deliberately scoped to that context.

For outbound URLs influenced by users, combine permitted schemes and destinations with network-level egress controls and a deliberate redirect policy. Parsing a URL or filtering one spelling of localhost is insufficient to establish a safe destination across DNS changes and redirects.

Verify TLS and protect secrets

Use the platform or framework's certificate-chain and hostname verification. When a connection fails, inspect the hostname, certificate validity, trust chain, server configuration and any approved enterprise proxy. Repair that configuration; do not replace it with a trust-all manager or disable hostname checks. The JSSE reference guide explains trust managers, key material and secure connections.

A useful staging test includes a valid connection, rejection of an untrusted certificate and rejection of a hostname mismatch. If the organization uses a private CA, distribute its trust through the approved trust-store process. Record who owns rotation. Dumping a certificate into a trust store without establishing its provenance turns a troubleshooting shortcut into a trust decision.

Obtain secrets through an approved runtime mechanism, give each service the privileges it needs, and rotate credentials using a documented procedure. Review exception logs, HTTP debug output and diagnostic captures for tokens or passwords. Environment variables can be a delivery mechanism but still need protection from process inspection and accidental logging. Avoid putting secrets in source, URLs or command-line arguments.

Use a maintained authentication framework and an established password-storage scheme when the application owns passwords. OWASP's password-storage guidance explains password hashing and work factors. General-purpose message digests and home-designed encryption are poor substitutes for that purpose.

Replace legacy Security Manager assumptions

The Security Manager was permanently disabled in JDK 24. Code that depended on enabling it or installing one must be reassessed before moving to Java 25. JEP 486 documents the change and affected APIs. A historical policy file no longer supplies the same runtime enforcement on this baseline.

Search application code, startup scripts and configuration for SecurityManager, java.security.manager, java.security.policy, doPrivileged and custom permission assumptions. Classify each finding: obsolete compatibility code, an assumption about library access, or a real boundary intended to contain untrusted code. Then remove obsolete startup settings or redesign the required boundary and test it.

Use process, operating-system, container or stronger isolation controls appropriate to the threat model for executing untrusted components. Give the process a restricted identity and explicit file and network access. A container label by itself says little about its privileges; review its actual configuration. Applications that accept arbitrary executable code require a dedicated containment design.

Java object deserialization is another boundary to inventory. Prefer an explicit data format with a schema when you control the interface. If object serialization is unavoidable, restrict permitted classes and graph size, depth and reference counts using serialization filtering, and test rejected inputs. A filter supplements the trust design; it does not establish that every permitted object's behavior is safe.

Make the review part of a release decision

Capture the resolved dependency graph from the build, scan it with the organization's chosen tools and triage findings against actual deployed versions and usage. Pinning a dependency makes the build reproducible; it does not freeze its security status. Update and retest when runtime patches, dependencies, permissions or trust boundaries change.

For each unresolved issue, record impact, owner, mitigation and an expiry for any accepted exception. Keep evidence of both successful authorized behavior and rejected unauthorized behavior. After release, verify that the deployed process uses the intended runtime and identity, and that security-relevant events are visible without exposing sensitive payloads.

If AI helps review code or interpret a dependency report, use redacted examples and verify proposed fixes against the actual framework and test suite. The reviewer remains responsible for the policy, evidence and release decision.

Continue with a related task