HIGH bola idorspring bootbearer tokens

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 IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Can BOLA still occur if I use role-based annotations like @PreAuthorize("hasRole('USER')") without ownership checks?
Yes. Role-based checks alone do not prevent BOLA because they authorize by role, not by resource ownership. An authenticated user can still change numeric IDs to access other users’ data unless you also enforce that the resource belongs to the authenticated principal.
Does including the user ID in every repository query impact performance or expose user enumeration risks?
Including the user ID in queries is a necessary mitigation for BOLA and should not materially affect performance if indexed properly. To reduce enumeration risks, ensure error messages are generic (e.g., not revealing whether a record exists) and use opaque identifiers (UUIDs) instead of sequential IDs where feasible.