Broken Access Control in Spring Boot with Basic Auth
Broken Access Control in Spring Boot with Basic Auth — how this specific combination creates or exposes the vulnerability
Broken Access Control (BOLA/IDOR) in Spring Boot applications using HTTP Basic Auth commonly occurs when authorization checks are missing or incorrectly applied after authentication. With Basic Auth, credentials are sent in an encoded (not encrypted) header on each request. If endpoints rely only on authentication (verifying who you are) and omit authorization (verifying what you are allowed to do), attackers can manipulate identifiers in URLs or parameters to access other users' resources.
For example, consider a Spring Boot REST endpoint like /api/users/{userId}/profile. If the application authenticates the request with Basic Auth but does not verify that the authenticated user matches the {userId} in the path, an authenticated user can change the ID and browse other profiles. This is a classic BOLA/IDOR scenario. The unauthenticated attack surface is particularly relevant for Basic Auth because scanning tools can send valid credentials and still probe for IDOR by iterating resource identifiers.
Spring Boot’s method-level security (e.g., @PreAuthorize) and URL-based security (e.g., antMatchers) must align with domain ownership rules. Common mistakes include:
- Exposing repository methods that return data based only on an identifier without checking the authenticated user.
- Relying on UI-level hiding of links instead of enforcing server-side checks.
- Using role-based checks alone (e.g., hasRole('ADMIN')) when the rule should be user-to-resource ownership.
Because Basic Auth transmits credentials in an easily reversible format, it is also important to ensure transport encryption (TLS). Without TLS, intercepted credentials and session tokens can be reused. In a black-box scan, an authentication check may pass, but a BOLA scan can still uncover missing authorization logic by probing other users’ IDs once authenticated.
Basic Auth-Specific Remediation in Spring Boot — concrete code fixes
Remediation focuses on enforcing ownership checks and avoiding over-privileged roles. Below are specific fixes and code examples tailored for Basic Auth in Spring Boot.
1. Use Authentication-aware queries
Always resolve the current user from the SecurityContext and use it in queries rather than trusting path parameters. Here is a secure controller pattern:
@RestController
@RequestMapping("/api/users")
public class UserProfileController {
private final UserProfileRepository userProfileRepository;
public UserProfileController(UserProfileRepository userProfileRepository) {
this.userProfileRepository = userProfileRepository;
}
@GetMapping("/{userId}/profile")
public ResponseEntity<UserProfileDto> getProfile(@PathVariable UUID userId, Authentication authentication) {
String authenticatedUsername = authentication.getName(); // e.g., the username used in Basic Auth
UserProfile profile = userProfileRepository.findByUsernameAndUserId(authenticatedUsername, userId);
if (profile == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(toDto(profile));
}
}
The repository method should enforce ownership:
@Repository
public interface UserProfileRepository extends JpaRepository<UserProfile, UUID> {
@Query("SELECT u FROM UserProfile u WHERE u.userId = :userId AND u.username = :username")
Optional<UserProfile> findByUsernameAndUserId(@Param("username") String username, @Param("userId') UUID userId);
}
2. Method-level security with domain ownership
Use @PreAuthorize to express ownership rules clearly. This ensures checks are applied consistently:
@Service
public class UserProfileService {
private final UserProfileRepository userProfileRepository;
public UserProfileService(UserProfileRepository userProfileRepository) {
this.userProfileRepository = userProfileRepository;
}
@PreAuthorize("@profileOwnershipValidator.isOwner(authentication, #userId)")
public UserProfileDto getProfile(UUID userId, Authentication authentication) {
// fetch and DTO conversion
return ...;
}
}
Implement a validator bean:
@Component
public class ProfileOwnershipValidator {
@Autowired
private UserProfileRepository userProfileRepository;
public boolean isOwner(Authentication authentication, UUID userId) {
String username = authentication.getName();
return userProfileRepository.existsByUsernameAndUserId(username, userId);
}
}
3. Secure Basic Auth configuration with TLS enforcement
Ensure HTTP is redirected to HTTPS and Basic Auth is configured with role-based entry points:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.requiresChannel(channel -> channel
.anyRequest().requiresSecure() // enforce TLS
)
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/api/users/**").authenticated()
.anyRequest().denyAll()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("alice")
.password("secret') // in production, use a proper PasswordEncoder
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
Remediation guidance includes:
- Never trust path or query parameters for access control — always cross-check with the authenticated identity.
- Prefer method-level security for fine-grained rules and ensure repository methods filter by username or tenant ID.
- Use HTTPS to protect Basic Auth credentials in transit; consider deprecating Basic Auth in favor of token-based flows where feasible.
- Combine role checks with ownership checks; roles alone are insufficient for BOLA prevention.
By aligning authentication with domain-specific authorization, Spring Boot applications using Basic Auth can mitigate BOLA/IDOR risks while maintaining straightforward credential handling.