Broken Access Control in Spring Boot
How Broken Access Control Manifests in Spring Boot
Broken Access Control in Spring Boot applications typically emerges through misconfigured security contexts, improper role assignments, and flawed authorization checks. The most common attack vectors exploit Spring Boot's default security configurations and the way it handles authentication and authorization.
One prevalent pattern involves endpoint exposure through controller annotations. Consider this vulnerable Spring Boot controller:
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // No authorization check!
}
}
This code allows any authenticated user to access any user's data by simply changing the ID parameter—a classic IDOR (Insecure Direct Object Reference) vulnerability. The absence of an authorization check before accessing the user service creates a direct path to data exposure.
Another Spring Boot-specific manifestation occurs with method-level security misconfigurations. When using @PreAuthorize or @PostAuthorize annotations, developers often make critical errors:
@Service
public class OrderService {
@PreAuthorize("hasRole('USER')") // Wrong role!
public Order getOrderDetails(Long orderId) {
return orderRepository.findById(orderId);
}
}
Here, the role check is insufficient—it only verifies the user has a USER role but doesn't confirm ownership of the order. An attacker with any USER role can access any order in the system.
Spring Boot's default security configuration can also introduce vulnerabilities. The default security auto-configuration might enable basic authentication without proper role-based access control:
// application.properties
security.basic.enabled=true
security.user.name=admin
security.user.password=password
This configuration provides no granular access control and uses weak default credentials, creating multiple attack vectors.
Cross-site request forgery (CSRF) protection misconfiguration is another Spring Boot-specific issue. When @EnableWebSecurity is used without proper CSRF configuration, state-changing operations become vulnerable:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // Disables CSRF protection!
}
}
Disabling CSRF protection without implementing alternative safeguards leaves the application vulnerable to unauthorized state changes.
Property-based authorization flaws also appear frequently in Spring Boot applications. When using @ConfigurationProperties to bind external configuration, improper access controls can expose sensitive configuration:
@Component
@ConfigurationProperties(prefix = "app.security")
public class SecurityProperties {
private String adminPassword; // Exposed via /actuator/env!
private String secretKey;
// getters and setters
}
If actuator endpoints are exposed without proper authentication, these properties become accessible to anyone, potentially exposing admin credentials and encryption keys.
Spring Boot-Specific Detection
Detecting Broken Access Control in Spring Boot applications requires a combination of static analysis and dynamic testing. middleBrick's API security scanner provides comprehensive detection capabilities specifically tuned for Spring Boot applications.
For runtime detection, middleBrick scans Spring Boot endpoints without requiring authentication or credentials. The scanner identifies vulnerable patterns by testing each endpoint with various user contexts and parameter manipulations. For example, it tests whether changing ID parameters in URLs returns different users' data—a clear indicator of IDOR vulnerabilities.
The scanner specifically looks for Spring Boot actuator endpoints that might be exposed without proper authentication. Many Spring Boot applications accidentally expose /actuator/health, /actuator/env, or /actuator/configprops endpoints, which can reveal sensitive configuration details and system information.
middleBrick's detection includes testing for method-level security misconfigurations by attempting to access secured endpoints with different user roles. It verifies whether @PreAuthorize and @PostAuthorize annotations properly enforce authorization checks or if they contain logical errors that allow unauthorized access.
The scanner also tests for CSRF vulnerabilities by attempting state-changing operations without proper CSRF tokens. For Spring Boot applications that have disabled CSRF protection, middleBrick identifies these endpoints and flags them as high-risk.
Property authorization testing is another critical detection area. middleBrick scans for exposed configuration properties that might contain sensitive information like database credentials, API keys, or encryption keys. It specifically tests Spring Boot's configuration binding mechanisms to identify improperly secured properties.
For applications using Spring Security OAuth2 or JWT authentication, middleBrick tests whether the authorization context is properly validated throughout the request lifecycle. It checks for scenarios where authentication is verified but authorization is not, or where token validation is insufficient.
The detection process also includes testing for privilege escalation vulnerabilities. middleBrick attempts to access admin-only endpoints with regular user credentials to verify that role-based access controls are properly implemented and enforced.
middleBrick provides a comprehensive report with severity ratings for each finding, specific to Spring Boot's security context. The report includes the exact endpoint, the vulnerability type (IDOR, CSRF, privilege escalation, etc.), and actionable remediation steps tailored to Spring Boot's security framework.
Spring Boot-Specific Remediation
Remediating Broken Access Control in Spring Boot applications requires implementing proper authorization checks and security configurations. Here are specific remediation strategies using Spring Boot's native security features.
For IDOR vulnerabilities, implement method-level security with proper ownership verification:
@Service
public class UserService {
@PreAuthorize("#id == authentication.principal.id ||
hasRole('ADMIN')")
public User getUserById(@Param Long id) {
return userRepository.findById(id);
}
}
This ensures users can only access their own data unless they have admin privileges. The SpEL expression verifies that the requested ID matches the authenticated user's ID.
For endpoint-level security, use Spring Security's @EnableWebSecurity with proper configuration:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/users/**").authenticated()
.anyRequest().authenticated();
}
}
This configuration enables CSRF protection and defines granular access controls for different API endpoints.
For property-based authorization, use Spring Security's @Secured annotation:
@RestController
@RequestMapping("/api")
public class SecureController {
@GetMapping("/config")
@Secured("ROLE_ADMIN")
public SecurityProperties getConfig() {
return securityProperties;
}
}
This ensures only users with the ADMIN role can access sensitive configuration endpoints.
For OAuth2 and JWT implementations, implement proper token validation:
@Configuration
@EnableWebSecurity
public class OAuth2Config extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(
new JwtAuthenticationConverter() {
@Override
protected Collection extractAuthorities(
Jwt jwt) {
Collection authorities =
super.extractAuthorities(jwt);
// Add custom authorization logic
if (jwt.getClaimAsString("scope").contains("admin")) {
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
return authorities;
}
}
))
);
}
}
This custom JWT converter adds additional authorization logic based on token claims.
For actuator endpoint security, disable or secure sensitive endpoints:
// application.properties
management.endpoints.web.exposure.include=health,info
management.endpoint.env.enabled=false
management.endpoint.configprops.enabled=false
This configuration limits actuator endpoint exposure to only essential endpoints.
For comprehensive protection, implement a custom access decision manager:
public class CustomAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object,
Collection configAttributes)
throws AccessDeniedException {
for (ConfigAttribute attribute : configAttributes) {
if (attribute.getAttribute().equals("ROLE_ADMIN")) {
if (authentication.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
return;
}
}
// Additional custom authorization logic
}
throw new AccessDeniedException("Access denied");
}
}
This allows implementing complex, application-specific authorization logic beyond simple role checks.