Spring Boot Tutorial
Security Hardening Checklist for Spring Boot
Configuring authentication and authorization is only part of securing an application. Production incidents are just as often caused by an exposed Actuator endpoint, a verbose error page, a leaked secret, an outdated dependency or a missing rate limit.
This lesson collects the practical hardening steps experienced Spring Boot teams apply before going live, organised as a checklist you can run through for every service. Each item includes the configuration or code needed to implement it.
Transport and Headers
Serve everything over HTTPS, usually terminated at a load balancer. Set server.forward-headers-strategy=framework so Spring knows the original request was HTTPS, enable HSTS, and add a Content Security Policy for server-rendered pages. Spring Security already sends X-Content-Type-Options, X-Frame-Options and cache-control headers by default — do not disable them.
Secrets Management
Never commit passwords, API keys or signing keys. Read them from environment variables, Kubernetes secrets, or a secret manager (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) via spring.config.import. Rotate secrets regularly and immediately after anyone leaves the team or a leak is suspected. Add a secret scanner (GitHub secret scanning, gitleaks) to CI.
Actuator and Error Exposure
Expose only the Actuator endpoints you need (typically health, info, prometheus), preferably on a separate management port that is not reachable from the internet, and require authentication for the rest. Never expose env, heapdump or configprops publicly — they can reveal secrets. Keep server.error.include-stacktrace=never and return ProblemDetail responses without internal details.
Input Handling
Validate every request with Bean Validation, use parameterised queries (Spring Data and JdbcClient do this for you — never concatenate SQL), limit upload sizes, and encode output in templates (Thymeleaf escapes by default; avoid th:utext with user data). For outbound HTTP calls to user-supplied URLs, block internal addresses to prevent SSRF; Spring Boot 4.1 adds an InetAddressFilter for its HTTP clients.
Abuse Protection
Login, registration, password reset and OTT endpoints need rate limiting to stop credential stuffing and enumeration. Use a gateway or a library such as Bucket4j, lock or slow down accounts after repeated failures, and log authentication events. Spring Security publishes AuthenticationSuccessEvent and AbstractAuthenticationFailureEvent, which you can listen to for auditing.
Dependencies and Supply Chain
Most vulnerabilities in Java applications come from dependencies. Stay on a supported Spring Boot line, update patch versions promptly, and scan dependencies in CI with OWASP Dependency-Check, Snyk, GitHub Dependabot or similar. Generate an SBOM (Spring Boot exposes one via the sbom Actuator endpoint when you use the CycloneDX plugin) so you can quickly answer "are we affected?" when a new CVE is announced.
Examples
Production-oriented application.yml
server:
forward-headers-strategy: framework
error:
include-stacktrace: never
include-message: never
servlet:
session:
cookie:
secure: true
http-only: true
same-site: lax
spring:
config:
import: optional:vault:// # secrets from HashiCorp Vault (spring-cloud-vault)
servlet:
multipart:
max-file-size: 5MB
max-request-size: 10MB
management:
server:
port: 9090 # internal-only port
endpoints:
web:
exposure:
include: health, info, prometheus
endpoint:
health:
show-details: when-authorized
(Actuator now answers only on port 9090, exposes three endpoints, and error responses no longer leak stack traces.)
Security headers: HSTS and Content Security Policy
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.headers(headers -> headers
.httpStrictTransportSecurity(hsts -> hsts
.includeSubDomains(true)
.maxAgeInSeconds(31536000))
.contentSecurityPolicy(csp -> csp
.policyDirectives("default-src 'self'; img-src 'self' data:; script-src 'self'; frame-ancestors 'none'"))
.referrerPolicy(rp -> rp.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)))
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
return http.build();
}
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
Content-Security-Policy: default-src 'self'; img-src 'self' data:; script-src 'self'; frame-ancestors 'none'
Referrer-Policy: strict-origin-when-cross-origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Auditing authentication failures
@Component
public class AuthenticationAuditListener {
private static final Logger log = LoggerFactory.getLogger(AuthenticationAuditListener.class);
@EventListener
public void onSuccess(AuthenticationSuccessEvent event) {
log.info("login_success user={}", event.getAuthentication().getName());
}
@EventListener
public void onFailure(AbstractAuthenticationFailureEvent event) {
// never log the submitted password
log.warn("login_failure user={} reason={}",
event.getAuthentication().getName(),
event.getException().getClass().getSimpleName());
}
}
// Spring Boot publishes these events automatically when this bean exists
@Bean
AuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
INFO login_success user=asha@webnest.in
WARN login_failure user=asha@webnest.in reason=BadCredentialsException
WARN login_failure user=admin reason=BadCredentialsException
Dependency vulnerability scanning in the Maven build
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>12.1.0</version>
<configuration>
<failBuildOnCVSS>7</failBuildOnCVSS>
</configuration>
</plugin>
# run in CI
mvn org.owasp:dependency-check-maven:check
[INFO] Analysis Complete
[ERROR] One or more dependencies were identified with vulnerabilities that have a CVSS score greater than or equal to '7.0':
[ERROR] example-lib-1.2.3.jar: CVE-2026-XXXXX (8.1)
[INFO] BUILD FAILURE
Common Mistakes
- Setting management.endpoints.web.exposure.include=* in production, exposing env and heapdump to the internet.
- Printing secrets at startup or logging full request bodies that contain passwords or tokens.
- Forgetting forward-headers-strategy behind a proxy, so redirects and OAuth2 callback URLs use http:// instead of https://.
- Leaving login and password-reset endpoints without any rate limit.
- Staying on an end-of-life Spring Boot version that no longer receives security patches.
Key Points to Remember
- Enforce HTTPS, HSTS and a Content Security Policy; keep Spring Security's default headers.
- Load secrets from the environment or a secret manager, never from committed files.
- Expose only necessary Actuator endpoints, ideally on an internal management port.
- Validate input, use parameterised queries, limit uploads and guard against SSRF.
- Rate-limit authentication endpoints, audit login events and scan dependencies continuously.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Use your local JDK or project IDE for these examples. Codelab currently runs Python and HTML/CSS/JavaScript; framework examples may need project dependencies.