Join our Newsletter — 33% off our NHI Course
Home› Guides› Java and Spring Security Guide: Securing Enterprise Java…
Guide Application Security

Java and Spring Security Guide: Securing Enterprise Java Applications

PDF for newsletter subscribers · free to read online

← All guides
By Lalit Choda, NHI Mgmt Group Updated 24 October 2026 18 min read
On this page

Enterprise Java applications fail less often because of the language and more often because of a framework default someone changed, a dependency nobody tracked or a management endpoint left open. Log4Shell and Spring4Shell showed how one library flaw can reach thousands of systems at once, and both remain in the CISA Known Exploited Vulnerabilities catalogue. This guide covers the parts of a Java and Spring estate that decide whether an application holds up: Spring Security configuration, Spring Boot Actuator, deserialisation and expression injection, dependency patching, keystores and the credentials Java services carry. It is written for application security engineers, platform teams and developers who own Spring Boot services in production, and it assumes you already know the basics of web application risk.

Key takeaways

  • Deny by default, then open up. Define every SecurityFilterChain deliberately, end it with a catch-all rule and add method security for object-level checks, because unannotated methods are not protected.
  • Actuator is an admin interface. Only health is exposed over HTTP by default; every endpoint you add to management.endpoints.web.exposure.include needs authentication, a role and ideally a separate port or network path.
  • Untrusted data must never choose a class or an expression. Java deserialisation, SpEL, JNDI lookups and XML external entities all turn input into behaviour; filter with jdk.serialFilter and keep input out of evaluated expressions.
  • Your real attack surface is the dependency tree. Log4j and Spring Framework flaws were exploited within days of disclosure, so an SBOM, a remediation time frame and the ability to rebuild every service quickly matter more than any single control.
  • Keystores and service credentials are identities. JKS and PKCS12 files, database passwords in application.properties and tokens in environment variables belong in a secrets manager with owners and rotation.

The Java and Spring attack surface

A typical enterprise Java service is a Spring Boot application on a JVM, packaged as an executable jar or a WAR on a servlet container such as Apache Tomcat, pulling in dozens to hundreds of transitive dependencies. Each layer has its own class of failure.

LayerTypical weaknessKnown exploited example (CISA KEV)Primary control
Logging libraryAttacker-controlled strings evaluated as lookupsCVE-2021-44228, Apache Log4j2 remote code execution (added 10 December 2021, known ransomware use)Upgrade, SBOM-driven inventory, egress filtering from application servers
Web framework data bindingRequest parameters bound to unintended object propertiesCVE-2022-22965, Spring Framework JDK 9+ remote code execution (added 4 April 2022)Patch, restrict bindable fields, prefer executable jar packaging
Expression evaluationUser input reaching SpEL or other expression enginesCVE-2022-22963, Spring Cloud Function remote code execution (added 25 August 2022)Never evaluate input as an expression; patch
Identity middlewareLegacy, internet-facing Java middleware left unpatchedCVE-2021-35587, Oracle Fusion Middleware (added 28 November 2022)Patch or retire; decommission legacy login servers fully
SerialisationNative Java deserialisation of untrusted streams (CAPEC-586 Object Injection)Many gadget-chain CVEs across librariesObjectInputFilter allowlists, avoid native serialisation at trust boundaries
Management planeActuator endpoints such as env or heapdump reachable without authenticationConfiguration error rather than a CVEMinimal exposure, Spring Security on EndpointRequest.toAnyEndpoint()
Secrets and keystoresPasswords in properties files, default keystore passwords, keys in imagesConfiguration errorSecrets manager, PKCS12 with unique passwords, rotation

Insecure deserialisation has its own guide on this site, so this page covers only the Java-specific controls. See the Insecure Deserialisation Guide.

Spring Security configuration that holds up

Request-level authorisation

  • Own the filter chain: once you declare your own SecurityFilterChain bean, Spring Boot's default security auto-configuration backs off. That is intended, but it means anything you forget to cover is no longer covered by a default. Review every chain as code.
  • End with a catch-all: the Spring Security documentation recommends a final rule in HttpSecurity so every request matches at least one rule. In practice that is anyRequest().authenticated() or anyRequest().denyAll() after the explicit permits, never anyRequest().permitAll().
  • Order matters: matchers are evaluated in order, so a broad permitAll() early in the chain silently overrides stricter rules below it. Put specific paths first and test the negative cases.

Method security for object-level checks

  • Turn it on explicitly: @EnableMethodSecurity is not enabled by the Spring Boot security starter. It enables @PreAuthorize, @PostAuthorize, @PreFilter and @PostFilter; @Secured and JSR-250 annotations need securedEnabled or jsr250Enabled, and the documentation recommends @PreAuthorize over both.
  • Use it for ownership: expressions such as returnObject.owner == authentication.name are the documented way to stop insecure direct object references. Do not use @PostAuthorize on methods that write, because the change happens before the check runs.
  • Know the proxy limit: method security is built on Spring AOP proxies, so a call from one method to another on the same bean skips the proxy and the second method's annotation is not evaluated. Put authorisation on the public entry point or move the protected method to a separate bean.
  • Unannotated means unprotected: method security only applies where annotations exist. Keep request-level rules as the coarse layer and method rules as the fine layer, not one instead of the other.

CSRF, cookies and sessions

  • Keep CSRF protection on for browsers: the Spring Security documentation says to disable it only for services used exclusively by non-browser clients. A JSON API that accepts a session cookie or HTTP Basic credentials from a browser is still exposed, including to enctype="text/plain" form posts, so validate Content-Type as well.
  • Keep safe methods safe: GET, HEAD, OPTIONS and TRACE must not change state, because both the synchroniser token pattern and SameSite protection depend on it.
  • Set SameSite yourself: Spring Security does not set the SameSite attribute on the session cookie; Spring Session can. Use Lax or Strict with Secure and HttpOnly as defence in depth, not as the only CSRF control.

Password storage

  • Use DelegatingPasswordEncoder: it is the default, stores hashes as {id}encodedPassword and lets you add a stronger algorithm later while still verifying old hashes. The default encoder is bcrypt with strength 10.
  • Tune the work factor: the documentation recommends about one second per verification on your hardware, then exchanging the password for a short-lived session or token rather than checking it on every request.
  • Never ship NoOpPasswordEncoder: it stores plain text. To migrate legacy hashes, prefix them with the correct id rather than falling back to no encoding.
  • Choose by constraint: PBKDF2 is the documented choice where FIPS certification is required; Argon2 and scrypt are memory-hard alternatives (Argon2 needs BouncyCastle).

Spring Boot Actuator and production profiles

Actuator is the most common way a well-written Spring Boot application leaks configuration. Treat every endpoint beyond health as an administrative API.

Property or endpointDefault (Spring Boot 4.x documentation)Recommended production setting
management.endpoints.web.exposure.includehealthList endpoints explicitly; never * on an internet-facing port
management.endpoints.web.exposure.excludeEmpty (exclude wins over include)Exclude env, configprops, heapdump, threaddump, beans, logfile unless needed
management.endpoints.access.defaultUnrestricted, except shutdown and heapdumpnone, then opt in per endpoint with management.endpoint.<id>.access
management.endpoints.access.max-permittedNot setread-only in production so no endpoint can be made writable by accident
management.endpoint.env.show-values (also configprops, quartz)Values sanitised to ******never, or when-authorized with a named role in management.endpoint.env.roles
heapdumpWeb only, access restrictedLeave restricted; a heap dump can contain secrets, tokens and session data in memory
shutdownDisabled, jar packaging onlyLeave disabled
  • Secure the endpoints with their own chain: if Spring Security is present and you define no chain, Boot secures every actuator endpoint except /health. Once you define your own chain, add one scoped with securityMatcher(EndpointRequest.toAnyEndpoint()) that requires a dedicated role.
  • Separate the network path: run management endpoints on a different port or behind an internal-only route so the public load balancer never forwards /actuator/**.
  • Use profiles carefully: Spring profiles such as dev often enable H2 consoles, debug logging, permissive CORS or wide actuator exposure. Fail the build or start-up if a development profile is active in production, which also satisfies OWASP ASVS 5.0 V13.4.2 (debug modes disabled in production).
  • Hide versions: remove server and framework version banners and error-page stack traces (ASVS V13.4.6), so attackers cannot match you to a CVE list in one request.

Deserialisation, expressions and lookups

The most damaging Java flaws share one pattern: input that should have been data was treated as instructions.

  • Native deserialisation: avoid ObjectInputStream on any data that crosses a trust boundary. Where you cannot, set a filter. JEP 290 (JDK 9) added ObjectInputFilter and the process-wide jdk.serialFilter system or security property. Patterns combine limits (maxdepth, maxrefs, maxbytes, maxarray) with class rules, where com.example.dto.** accepts a package tree and a leading ! rejects. End an allowlist with !* so anything unlisted is rejected. Filter decisions are logged to the java.io.serialization logger, which is useful in audit mode. ASVS V1.5.2 requires an allowlist of object types for untrusted deserialisation.
  • Polymorphic JSON and XML: the same risk returns when a JSON or XML library lets the payload name the class to instantiate. Disable default typing, bind to concrete DTOs and configure XML parsers so external entities are off (ASVS V1.5.1). See the XXE Guide.
  • Expression languages: SpEL, OGNL and template engines are code. Never build an expression string from request data, and keep user input out of @PreAuthorize expressions, routing headers and dynamic queries. See the Server-Side Template Injection (SSTI) Guide.
  • JNDI and lookups: Log4Shell worked because Log4j evaluated ${jndi:...} lookups in log messages and could load code from an attacker-controlled LDAP server. Beyond patching, deny outbound LDAP, RMI and arbitrary HTTP from application servers so a future lookup flaw has nowhere to call. Encode logged data to prevent log injection (ASVS V16.4.1).
  • Data binding: Spring4Shell abused data binding to reach class loader properties. Bind requests to purpose-built DTOs, restrict bindable fields with @InitBinder and setAllowedFields where you must bind to domain objects, and avoid binding directly to JPA entities.

Dependencies and patching: what Log4Shell and Spring4Shell taught

VulnerabilityAffectedFixed inConditions and notes
CVE-2021-44228 (Log4Shell)Log4j 2.0-beta9 to 2.14.x, other than the fixed 2.12.2 and 2.3.1 releases2.15.0 (Java 8), 2.12.2 (Java 7), 2.3.1 (Java 6)JNDI lookups in configuration, log messages and parameters
CVE-2021-45046Incomplete fix for 442282.16.0 (Java 8), 2.12.3 (Java 7), 2.3.1 (Java 6)Non-default Pattern Layouts with Thread Context lookups
CVE-2021-45105 and CVE-2021-44832Later Log4j 2 releasesCurrent Log4j 2.x releaseDenial of service through recursive lookups; JDBC Appender JNDI risk requiring write access to logging configuration
CVE-2022-22965 (Spring4Shell)Spring Framework 5.3.0 to 5.3.17, 5.2.19 and earlier5.3.18, 5.2.20JDK 9+, Tomcat, WAR deployment, spring-webmvc or spring-webflux; executable jars not exposed to the published exploit, but Spring warned the underlying issue was more general
  • Patch in waves, not once: Log4j needed several releases in quick succession because the first fixes were incomplete.
  • Know where the jar is: Log4j was often a transitive dependency, shaded inside another jar or bundled in a vendor appliance. Maintain an SBOM per build (ASVS V15.1.2), scan images and file systems, not just pom.xml, and include third-party Java products in the inventory. See the SBOM Guide.
  • Set remediation time frames: ASVS V15.1.1 asks for documented, risk-based time frames for vulnerable components and V15.2.1 for proof the application meets them. Treat a KEV listing as the top tier. See the Vulnerability Prioritisation Guide.
  • Stay on supported lines: unsupported Spring, Tomcat and JDK versions stop receiving public fixes. Track end-of-support dates for each line you run and budget the major upgrades (for example javax to jakarta namespaces) before the deadline, not after.
  • Control where artefacts come from: resolve dependencies through an internal repository proxy so every component comes from an expected source (ASVS V15.2.4). See the Malicious Package and Dependency Confusion Guide.

Where identity fits: keystores, secrets and service credentials

Every Java service is a non-human identity. It holds database passwords, API keys, OAuth client secrets and private keys, usually more than any human user of the system.

  • Keystores: since JDK 9 (JEP 229) new keystores are PKCS12 by default; existing JKS files keep working. Give each keystore a unique, generated password stored in a secrets manager, never the well-known default of the JDK cacerts truststore, and never bake private keys into container images.
  • Properties files: passwords in application.properties, application.yml or profile-specific files end up in Git, images and Actuator env output. Load them at runtime from a vault or a cloud secrets service (ASVS V13.3.1). See the Secrets Management Guide.
  • Workload identity over static keys: where the platform supports it, let the service authenticate to cloud APIs and databases with short-lived workload credentials rather than long-lived keys. See the NHI Authentication Guide.

Choosing controls: decision guidance

DecisionChoose A whenChoose B when
Executable jar vs WAR on a shared servlet containerJar: you want one patched runtime per service and smaller blast radiusWAR: a platform team runs a hardened, centrally patched container and you can keep it current
Request rules vs method securityRequest rules: coarse access by path, role or authentication stateMethod security: ownership and object-level checks that depend on the data returned
bcrypt vs PBKDF2 vs Argon2bcrypt: the default, widely supportedPBKDF2 for FIPS requirements; Argon2 where memory-hard hashing is wanted and BouncyCastle is acceptable
Native serialisation vs explicit formatsNative, filtered: legacy RMI or caching between trusted components onlyJSON or protobuf bound to concrete types: anything crossing a trust boundary

Small teams: standardise on one Spring Boot baseline (a parent POM or Gradle convention plugin) that sets actuator exposure, security defaults and dependency versions, then let dependency update bots raise pull requests. Large estates: add a central SBOM store, a policy that blocks builds with KEV-listed components, a shared library of approved SecurityFilterChain patterns and an inventory of every Java product bought from vendors, because those were the slowest to patch during Log4Shell.

How to implement: a phased approach

  1. Inventory (weeks 1 to 2): list every Java service and product, its JDK, Spring and container versions, packaging type and internet exposure. Generate SBOMs for your own builds and scan file systems and images for bundled jars.
  2. Close the urgent gaps: patch KEV-listed components first, remove actuator exposure beyond health on public paths and block outbound LDAP and RMI from application servers.
  3. Set the baseline: publish a shared configuration with a deny-by-default filter chain, method security enabled, management.endpoints.access.default=none, sanitised env values and a production profile guard.
  4. Test in audit mode: deploy a jdk.serialFilter with logging to a pilot service and review the java.io.serialization log for classes you need before switching to reject. Run negative authorisation tests (anonymous, wrong role, other user's object) in CI.
  5. Roll out in rings: internal services first, then lower-risk external services, then critical ones. Keep the previous configuration version tagged so a rollback is a redeploy, not a rewrite.
  6. Move secrets out: migrate passwords and keys from properties files and images to a secrets manager, rotate each one as you move it, and remove the old values from Git history where possible.
  7. Keep it current: enforce remediation time frames in the pipeline, review end-of-support dates quarterly and rehearse a "new critical library CVE" drill once a year.

Detection and measurement

  • Log authorisation failures: Spring Security AuthorizationDeniedEvent and authentication failure events, forwarded to your SIEM with user, path and source.
  • Watch the management plane: alert on any request to /actuator/** from outside the management network and on heapdump or env access at all.
  • Watch egress: outbound LDAP, RMI or unexpected HTTP from application servers was the clearest Log4Shell signal and remains a good indicator of injection.
  • Watch for web shells and new processes: a JVM spawning shells or writing JSP files to the web root suggests exploitation (ATT&CK T1190 followed by T1505.003).
  • Log deserialisation rejections: filter rejections in production are either a missing allowlist entry or an attack, and both need attention.
MetricWhat good looks like
Services with an SBOM generated on every build100% of in-house Java services
Median time to remediate a KEV-listed Java componentWithin your documented top-tier time frame, measured from KEV addition
Internet-reachable actuator endpoints other than healthZero, verified by external scan
Services on an unsupported Spring, Tomcat or JDK lineFalling each quarter, with an upgrade date for every one
Plain-text secrets found in properties files and imagesZero new findings; legacy findings rotated and removed
Endpoints covered by negative authorisation testsAll state-changing and object-returning endpoints

Common failure patterns

  • A custom filter chain that forgot the catch-all: a new controller added later is reachable without authentication because no rule matched it.
  • exposure.include=* copied from a tutorial: env, configprops and heapdump become reachable, and a heap dump hands over every secret in memory.
  • Patching the pom but not the product: in-house code is upgraded while vendor appliances and shaded jars keep the vulnerable library for months.
  • Disabling CSRF for a "REST API" that browsers still call with cookies: the API is then open to cross-site requests.
  • Annotations on internal methods: @PreAuthorize on a method only ever called from the same bean does nothing.
  • Legacy middleware left running: an old login or SSO server on outdated Java middleware stays internet-facing with live keystores, as described in the Oracle Cloud claim below.

Lessons from incidents

In March 2025 an actor claimed to have taken around 6 million records, Java keystore (JKS) files and encrypted SSO and LDAP passwords from an Oracle Cloud login server. Researchers reported the server ran Oracle Fusion Middleware 11g and pointed to CVE-2021-35587, a KEV-listed Oracle Access Manager flaw, though that route was not confirmed and Oracle denied a breach of Oracle Cloud Infrastructure, reportedly telling customers that two obsolete servers were accessed. Whatever the final account, the lesson for Java estates is the same: legacy identity middleware with live keystores is part of your attack surface until it is fully decommissioned. Read the Oracle Cloud Breach Claim 2025 analysis.

Framework mapping

AreaNIST SP 800-53 Rev. 5NIST CSF 2.0OWASP ASVS 5.0Other
Secure configuration of Spring Security and ActuatorCM-7 Least Functionality, AC-6 Least PrivilegePR.PS-06V13.4.2, V13.4.6CIS Controls v8.1 Control 16; ISO/IEC 27001:2022 A.8.9, A.8.28
Deserialisation and injectionSA-11 Developer Testing and Evaluation, SA-15PR.PS-06V1.5.1, V1.5.2, V16.4.1CAPEC-586; ATT&CK T1190
Dependency and patch managementSI-2 Flaw Remediation, RA-5, SR-3ID.AM-02V15.1.1, V15.1.2, V15.2.1CIS Controls v8.1 Control 7; ISO/IEC 27001:2022 A.8.8
Secrets and keystoresIA-5 Authenticator Management, SC-12PR.AA-01V13.3.1ATT&CK T1552.001
Detection of exploitationSI-4 System MonitoringDE.CM-09V16 (logging)ATT&CK T1505.003

Practitioner checklist

  • Every SecurityFilterChain ends with a deny or authenticated catch-all, and negative authorisation tests run in CI.
  • @EnableMethodSecurity is on and object-level checks sit on public entry points, not self-invoked methods.
  • Actuator exposes only what is needed, with management.endpoints.access.default=none, a dedicated role and no public route to /actuator/**.
  • CSRF protection stays on for every browser-facing endpoint, and SameSite is set on the session cookie.
  • Passwords use DelegatingPasswordEncoder with a tuned work factor; NoOpPasswordEncoder appears nowhere.
  • A jdk.serialFilter allowlist is in place wherever native deserialisation remains, and XML parsers have external entities disabled.
  • Each build produces an SBOM, KEV-listed components block release, and vendor Java products are in the inventory.
  • Application servers cannot make outbound LDAP or RMI connections.
  • No secrets in properties files, images or Git; keystores have unique passwords held in a secrets manager.
  • Every Spring, Tomcat and JDK line in use has a known end-of-support date and an upgrade plan.

Standards and references

Related NHI Mgmt Group resources: Insecure Deserialisation Guide · OWASP ASVS Guide · Web Application Attacks Guide · Software Supply Chain Security Guide · Secrets Management Guide

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Written and reviewed by Lalit Choda, NHI Mgmt Group. Last updated 24 October 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.