Bola Idor in Spring Boot with Bearer Tokens
Bola Idor in Spring Boot with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Broken Object Level Authorization (BOLA) is a common API security risk where one user can access or modify another user’s resources by manipulating object identifiers. In a Spring Boot API that uses Bearer Tokens for authentication, BOLA often occurs when authorization checks are incomplete or inconsistent between endpoints.
Bearer Tokens typically identify the caller and are validated before requests reach controller logic. If your controllers use the token to authenticate the user but then rely only on the resource’s primary key (e.g., /users/{id}) without confirming that the resource belongs to the authenticated subject, the API is vulnerable. An attacker can change {id} to another user’s identifier and, if no ownership verification exists, gain unauthorized access to or perform actions on that other user’s data.
Consider a typical Spring Boot setup where JWT Bearer Tokens are validated and a UserDetails is available via SecurityContext. A controller like the following exposes BOLA because it trusts the path variable without checking ownership:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{userId}")
public ResponseEntity<UserDto> getUserById(@PathVariable Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
return ResponseEntity.ok(toDto(user));
}
}
In this example, any authenticated user with a valid Bearer Token can request /api/users/123 and, if a user with ID 123 exists, receive that user’s data. There is no check that the authenticated user’s ID matches 123. The same gap can appear in endpoints that modify resources, such as PUT or DELETE, enabling unauthorized updates or deletions. BOLA is often discovered during unauthenticated scans because the API exposes predictable numeric or UUID identifiers without access controls tied to the caller’s identity.
Spring Boot applications frequently use method-level security with annotations like @PreAuthorize, but if these are missing or misapplied, the risk increases. For instance, using only hasRole("USER") without a domain-specific check such as @PostFilter or a custom service that validates resource ownership is insufficient. Attackers do not need to break authentication; they only need to tamper with identifiers, knowing that the API does not enforce per-request ownership tied to the Bearer Token’s subject.
Because middleBrick scans the unauthenticated attack surface, it can detect endpoints where object identifiers are predictable and no evidence of ownership verification exists in the contract or runtime behavior. The scanner flags these findings under BOLA/IDOR, emphasizing the need to align authorization with the authenticated subject rather than relying solely on role-based access or missing checks.
Bearer Tokens-Specific Remediation in Spring Boot — concrete code fixes
To fix BOLA when using Bearer Tokens in Spring Boot, ensure that every data access decision includes a verification that the resource belongs to the authenticated principal. This typically means extracting the user identifier from the token and incorporating it into repository queries or security expressions.
One robust pattern is to use a domain object filter that always scopes queries by the current user. For example, instead of finding a user by ID alone, include the authenticated user’s ID in the lookup:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{userId}")
public ResponseEntity<UserDto> getUserById(@PathVariable Long userId,
Authentication authentication) {
String principalId = authentication.getName(); // or extract UUID/subject from token
if (!principalId.equals(userId.toString())) { // or use a service to fetch and compare
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
}
User user = userRepository.findByIdAndOwnerId(userId, principalId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
return ResponseEntity.ok(toDto(user));
}
}
In this approach, the repository method userRepository.findByIdAndOwnerId should be implemented to include both the resource ID and the owner identifier (e.g., user UUID or username) in the WHERE clause, ensuring that even if an ID is guessed, the row must belong to the caller.
For method-level security, prefer @PreAuthorize with domain-aware expressions. If you store the user identifier in the token as a claim (e.g., sub), you can reference it in expressions:
@PreAuthorize("@securityService.isOwner(authentication, #userId)")
@DeleteMapping("/{userId}")
public ResponseEntity<Void> deleteUser(@PathVariable Long userId) {
userService.delete(userId);
return ResponseEntity.noContent().build();
}
Implement securityService.isOwner to resolve the user identifier from the authentication and compare it with the requested resource’s owner. This keeps authorization logic centralized and testable.
When using JWT Bearer Tokens, you can extract subject details in a custom filter or within your controller advice and store them in a security context. For example, after validating the token, set an Authentication that includes the user ID as a principal or details:
String subject = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject(); // typically a user ID or UUID
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(subject, null, AuthorityUtils.NO_AUTHORITIES);
SecurityContextHolder.getContext().setAuthentication(auth);
With this setup, you can consistently reference authentication.getName() across controllers and services to enforce ownership. Combine this with repository methods that filter by owner_id or similar, and you effectively mitigate BOLA for endpoints that use Bearer Tokens.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |