Spring Boot Security Best Practices 2025
Comprehensive guide to implementing security best practices in Spring Boot applications in 2025, covering HTTPS/TLS, CSRF protection, input validation, authentication, and more.
Introduction
Spring Boot is widely used for building Java web backends, but it often handles sensitive data and must meet strict compliance requirements. Recent incidents like the Spring4Shell zero-day exploit (2022) showed that even sample Spring code can be dangerously insecure. A breach can damage reputation, incur fines, and can also lead to customers losing trust.
To avoid these risks, software developers must build security from the start. This article outlines key Spring Boot security best practices for 2025. Each section covers a practice with example code, so developers at all levels can quickly apply them.
Enforce HTTPS/TLS
Always use HTTPS (SSL/TLS) to encrypt data in transit. Spring Boot lets you configure an SSL keystore in application.properties
, for example:
server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=yourpassword
server.ssl.key-password=yourpassword
Additionally, force all HTTP requests to redirect to HTTPS in Spring Security. For example, extend WebSecurityConfigurerAdapter
:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requiresChannel()
.requestMatchers(r -> r.getHeader("X-Forwarded-Proto") != null)
.requiresSecure() // Enforce HTTPS
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.csrf().and()
.formLogin().and()
.httpBasic();
}
CSRF Protection
Cross-Site Request Forgery (CSRF) attacks can hijack authenticated user actions. Spring Security enables CSRF protection by default. Do not disable it in production. For example, avoid code like:
http.csrf().disable(); // ✗ Don't disable CSRF in production
Input Validation
Never trust user input. Always validate it thoroughly to prevent injection and other attacks. Use Spring's @Valid
and Bean Validation annotations in your DTOs. For example:
import javax.validation.constraints.NotEmpty;
public class UserInput {
@NotEmpty(message = "Name cannot be empty")
private String name;
// getters and setters
}
Then in a controller:
@PostMapping("/users")
public ResponseEntity<?> createUser(@RequestBody @Valid UserInput input) {
// process validated input
}
Parameterize Your Queries
Avoid building SQL queries via string concatenation. Instead, use Spring Data repositories or JdbcTemplate
with parameters. For example, a JPA repository query safely binds the email:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.email = :email")
User findByEmail(@Param("email") String email);
}
This example uses named parameters (:email
), so the query is pre-compiled and safe against injection. Alternatively, with JdbcTemplate
you can do:
String sql = "SELECT * FROM users WHERE name = ?";
User user = jdbcTemplate.queryForObject(sql,
new Object[]{name}, new UserRowMapper());
Always parameterize queries or use Spring Data repositories. Never concatenate raw user input into SQL, which leaves you vulnerable to malicious payloads.
Protect Against XSS
Cross-Site Scripting (XSS) can inject malicious scripts into your pages. To prevent it: 1) set strict security headers and 2) sanitize/encode user content. With Spring Security, configure security headers like Content Security Policy (CSP):
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.headers(headers -> headers
.contentTypeOptions(new ContentTypeOptionsHeaderWriter())
.frameOptions(new FrameOptionsHeaderWriter(FrameOptionsHeaderWriter.XFrameOptionsMode.DENY))
.contentSecurityPolicy("default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';")
);
return http.build();
}
}
Authentication & Authorization
Use Spring Security to configure authentication providers and ensure only authorized users access endpoints. A typical setup uses UserDetailsService
with a strong password encoder:
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(myUserDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
This example tells Spring to load users from myUserDetailsService
and validate passwords with BCrypt, a strong hashing algorithm. For modern REST APIs, consider token-based auth. Methods like JWT (JSON Web Tokens) are popular for stateless APIs. Always serve tokens only over HTTPS and avoid exposing credentials in URLs.
Method-Level Security
Protect critical service methods with annotations. Spring allows you to put security checks right on methods via @PreAuthorize
or @RolesAllowed
. For example:
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
// deletion logic
}
This ensures only users with the ADMIN role can invoke deleteUser()
.
Encrypt Sensitive Data
Even with secure transit (HTTPS), data at rest can be at risk. Encrypt any sensitive properties or database fields. For example, using Jasypt's Spring integration, protect secrets in your config:
@EncryptablePropertySource(name="EncryptedProps", value="classpath:encrypted.properties")
@Configuration
public class EncryptionConfig {
// Jasypt decryption setup
}
Dependency Management and Updates
Keep all libraries and frameworks up-to-date. Many Spring Boot vulnerabilities come from outdated dependencies. Use Maven or Gradle's dependency management to pin secure versions. Tools like OWASP Dependency-Check or Trivy can scan your project for known CVEs. For example, integrate a scan into your CI pipeline:
# Example: Trivy can scan dependencies
trivy fs --security-checks vuln .
Logging & Auditing
Maintain detailed logs of security events. Spring Boot's Actuator and Spring Security's auditing support help here. Enable the audit and authentication event listeners:
public class CustomAuditListener extends AbstractAuthenticationAuditListener {
@Override
public void onApplicationEvent(AbstractAuthenticationEvent event) {
// e.g., log login failures or successes
logger.info("Auth event: " + event.toString());
}
}
Logging important security events (login failures, role changes, etc.) enables quick detection of attacks.