Safeguard
Application Security

10 Spring Boot security best practices

Ten concrete Spring Boot security practices, with real CVEs, config flags, and file paths, to close the gaps attackers actually exploit.

Bob
Application Security Engineer
Updated 7 min read

Spring Boot powers an estimated 60%+ of new Java microservices, according to JetBrains' 2023 developer survey — which also makes it one of the most consistently targeted frameworks in enterprise environments, and Spring Boot security a first-order concern for any Java team. In March 2022, CVE-2022-22965 ("Spring4Shell") let attackers achieve remote code execution on Spring MVC/WebFlux apps running on Java 9+ via a simple crafted HTTP request, with mass exploitation attempts recorded within 48 hours of disclosure. Nine months earlier, Log4Shell (CVE-2021-44228) hit an untold number of Spring Boot services because Spring's default logging stack pulled in vulnerable Log4j2 versions transitively. The same transitive-resolution trap applies to spring-security-core itself, the Maven/Gradle artifact underneath every spring-boot-starter-security project, and its companion module spring-security-crypto, which almost never gets pinned or scanned deliberately. Most Spring Boot breaches trace back to a small set of repeatable mistakes: exposed Actuator endpoints, hardcoded secrets, outdated dependencies, and permissive default security configs. This post covers ten concrete practices — with the CVEs, config flags, and file paths involved — so your team can close these gaps before an attacker finds them.

What are the most common Spring Boot security misconfigurations?

The most common misconfiguration is leaving Actuator's /env, /heapdump, and /mappings endpoints exposed on the default management port without authentication. Security researchers have repeatedly found production systems leaking database credentials, AWS keys, and session tokens through /actuator/env because teams add spring-boot-starter-actuator for health checks and monitoring, then forget it also ships debugging endpoints that dump the entire application context. A second common issue is spring.h2.console.enabled=true left active in production — the H2 database console, meant for local development, provides a SQL execution UI reachable at /h2-console with no default authentication in older Spring Boot versions. A third is CORS configured with allowedOrigins("*") combined with allowCredentials(true), a combination Spring Framework explicitly disallows at runtime as of Spring 5.3 but which developers work around by whitelisting overly broad wildcard patterns like *.yourdomain.com that unintentionally match attacker-registered subdomains.

How do you secure Spring Boot Actuator endpoints?

You secure Actuator by disabling endpoint exposure by default and explicitly allow-listing only what you need, rather than relying on management.endpoints.web.exposure.include=*. Set management.endpoints.web.exposure.include=health,info in application.yml and require authentication for everything else by mapping /actuator/** to a dedicated Spring Security filter chain with ROLE_ADMIN access. Move the management port off the application port entirely with management.server.port=8081 and bind it to 127.0.0.1 or an internal-only network interface so it's unreachable from the public internet — this single change would have prevented most of the actuator-exposure incidents cataloged in Shodan scans that turn up thousands of internet-facing /actuator/env endpoints at any given time. For Spring Boot 2.6+, also set management.endpoint.env.show-values=WHEN_AUTHORIZED so environment values are redacted unless the caller is authenticated, and enable management.endpoint.heapdump.enabled=false unless a specific incident-response workflow requires it.

How do you prevent dependency vulnerabilities like Log4Shell in Spring Boot apps?

You prevent them by generating and continuously scanning a software bill of materials (SBOM) against your dependency tree, not by waiting for a CVE announcement to check manually. Spring Boot's transitive dependency graph is deep — a typical spring-boot-starter-web project pulls in 40-80 transitive JARs — which is exactly why Log4Shell (CVE-2021-44228, CVSS 10.0) affected so many Spring Boot services that never directly declared a log4j-core dependency; it arrived through spring-boot-starter-logging chains or third-party libraries. Run mvn dependency:tree or the Maven/Gradle CycloneDX plugin on every build to produce an SBOM, and pin your Spring Boot parent version to at least 2.6.15, 2.7.18, or 3.1.x, all of which shipped patched logging and codec dependencies. Set up Dependabot or Renovate with a same-day SLA for critical CVEs specifically, since the gap between Log4Shell's December 10, 2021 disclosure and mass internet-wide scanning was measured in hours, not days.

How should you manage secrets and credentials in Spring Boot applications?

You manage secrets by never storing them in application.properties or application.yml inside version control, and instead injecting them at runtime from a dedicated secrets manager. GitHub's 2023 secret-scanning data showed database connection strings and API keys as consistently among the most common secret types found in public repositories, and Spring Boot's convention of centralizing config in a single readable properties file makes it an easy target for accidental commits. Use Spring Cloud Vault or AWS Secrets Manager's Spring Boot integration to pull credentials at startup, referencing them as ${sm://my-secret} rather than plaintext values. For local development, use spring.config.import=optional:file:.env[.properties] with a gitignored .env file, and add a pre-commit hook running gitleaks or trufflehog to catch secrets before they reach a remote branch — remediation after a push means rotating every exposed credential, since git history retains the value even after deletion.

How do you configure Spring Security for authentication and authorization correctly?

You configure it correctly by defining an explicit SecurityFilterChain bean with a default-deny rule, then layering specific permits on top, rather than relying on Spring Security's older auto-configuration defaults. In Spring Security 6 (bundled with Spring Boot 3.x), the WebSecurityConfigurerAdapter class was removed entirely in favor of component-based SecurityFilterChain beans — a breaking change that has led some teams migrating from Spring Boot 2.x to leave authorization rules incompletely ported, accidentally permitting endpoints that were previously restricted. Structure your chain as .authorizeHttpRequests(auth -> auth.requestMatchers("/api/public/**").permitAll().anyRequest().authenticated()) so the fallback is always authentication-required. Spring Security 6 splits this functionality across a few Maven artifacts worth knowing by name: spring-security-core, the Spring Security core module, holds the base authentication and authorization framework used above, while spring-security-crypto holds password-hashing utilities like BCryptPasswordEncoder and Argon2PasswordEncoder — pin both explicitly in your BOM rather than trusting whatever version spring-boot-starter-security happens to resolve. Enable CSRF protection by default for any endpoint serving browser sessions (Spring Boot disables it by default only for stateless REST APIs using token auth), and set server.servlet.session.cookie.same-site=Strict alongside Secure and HttpOnly flags to reduce session-hijacking exposure.

How do you harden a Spring Boot application for production deployment?

You harden it by disabling verbose error responses, enforcing TLS termination, and running the JVM with a non-root user in your container. Set server.error.include-message=never and server.error.include-stacktrace=never in production profiles — Spring Boot's default Whitelabel error page and stack trace output can leak internal class names, file paths, and library versions that speed up an attacker's reconnaissance. In your Dockerfile, add USER spring:spring after copying the JAR so the application never runs as root inside the container, a control that would have limited container-breakout impact in several published Java supply-chain incidents. Enforce HTTPS with server.ssl.enabled=true and pair it with HSTS via Spring Security's .headers(headers -> headers.httpStrictTransportSecurity(hsts -> hsts.maxAgeInSeconds(31536000))). Finally, separate Spring profiles cleanly — application-prod.yml should never inherit debug logging levels or the H2 console flag from a shared base file, since profile inheritance mistakes are a repeat cause of dev-only settings shipping to production.

How Safeguard Helps

Safeguard maps every CVE in your Spring Boot dependency tree against actual code paths using reachability analysis, so you're not chasing a patch for a vulnerable log4j-core method your application never calls — only the ones an attacker can actually reach. Griffin AI, Safeguard's security reasoning engine, reads the vulnerable function signature, traces call paths through your Spring MVC controllers and service layers, and tells your team in plain language whether Spring4Shell-style exploitation is possible in your specific configuration. Safeguard generates CycloneDX-format SBOMs automatically on every build (or ingests SBOMs you already produce via Maven/Gradle plugins) and continuously diffs them against new CVE feeds, flagging transitive dependencies like the ones that carried Log4Shell into unrelated Spring Boot services. When a fix is available, Safeguard opens an auto-fix pull request that bumps the affected dependency and its parent BOM version, with the reachability context attached so reviewers know exactly why the patch matters before merging.

Never miss an update

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