Spring Security - Securing Spring Boot API



Spring Security

Securing Spring Boot API is now not limited to having a Login Page but to provide a fine grain control from user accessing to particular page to particular controller function as well. It is treated as a multi-layered Defence in Depth strategy. From Spring Security 6+, the security architecture has moved from stateful to stateless and lambda style configurable specifications. In this chapter, we're discussing various layers of spring security used to secure spring boot APIs.

Core Architecture

Spring Boot APIs are exposed to end users using REST Controllers which are guarded using spring security filters as explained in Spring Security - Security Filter Chain Chapter. Now instead of using WebSecurityConfigurerAdapter class to create a filter chain, we've more granular control by creating a Security Configuration Component as shown below −

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

   @Bean
   public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
   
   return http
      .csrf(AbstractHttpConfigurer::disable)  // disabled for stateless APIs
      .authorizeHttpRequests(
         request -> request.requestMatchers("/public/**").permitAll() // Public endpoints
            .requestMatchers("/admin/**").hasRole("ADMIN")  // Admin-only endpoints
            .anyRequest().authenticated()
      )
	  .sessionManagement(session -> session
         .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
      .formLogin(Customizer.withDefaults())      
      .logout(config -> config  
      .logoutUrl("/logout") 
      .logoutSuccessUrl("/login"))
      .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))	  // Using OAuth
      .build();
   }
}

Authentication

Session management via session and cookies is the standard way of authentication in case of web applications. But for APIs, now this is not preferred being stateful. OAuth2 and JWT have changed the way of authentication to stateless and much more configurable.

OAuth2

OAuth2 is the gold standard for implementing the security protocol for Spring Boot APIs. Popular Identity providers are keyclock, Okta and Auth0. With OAuth2, We need to validate the JWT signature only and rest is handled by OAuth Service provider.

Custom JWT

In case, external JWT service provider is not to be used, then we can use libraries like jjwt to create and validate jwt tokens. Filter, OncePerRequestFilter is to be implemented and extract the token from http header Authorization Bearer <token>.

Authorization

Once a user is authenticated, access control is the next task to secure the APIs. Spring Security provides annotations to provide access control over service or controller layer methods. This makes it a more granular approach then url or path based access rules.

@Controller 
@EnableMethodSecurity
public class OrderController {

    OrderService orderService; 
 
    @GetMapping("/{id}")
    @PreAuthorize("hasAuthority('SCOPE_ORDER:read')")
    public Order getOrder(@PathVariable Long id) {
        return orderService.findById(id);
    }
   
   // User with Admin role only can access this method
   @DeleteMapping("/{id}")  
   @PreAuthorize("hasRole('ROLE_ADMIN')")  
   public void delete(@PathVariable Long id) {    
      orderService.delete(id)   
   }     
}

Best Practices

Following are the best practices to follow while implementing securing Spring Boot APIs.

  • HTTPS over HTTP − JWT or any other security token should be transported using HTTPS only. HTTP should never be used to transport sensitive data.

  • Password Hashing − Never store plain password. Use BCryptPasswordEncoder or Argon2 to encode the password before storing.

  • Secrets − JWT Secrets should never be hard coded in the application. It is always advisable to store and retrieve jwt secrets using spring cloud config. AWS or Azure key vaults are also perferred choices.

  • Check Vulnerabilities Regularly − OWASP Dependency Checks/Synk can be used to scan the spring boot starters for any listed CVE (Vulnerabilities).

  • Enable Security Headers − Enable strict-transport-security and x-content-type-options headers.

  • Rate Limiting − In order to prevent Brute Force/DDos attacks, APIs should have rate limiting enabled. You can use Bucked4j or API gateway to achieve the same.

  • Prevent SQL Injection Attack − Using Spring Data JPA to prevent SQL Injection attacks and Spring Data JPA uses PreparedStatement by default.

  • Mask Sensitive Data In Logs − Use Spring Boot Actuator to enable logging but ensure that any sensitive data is masked in logs.

Advertisements