Bola Idor in Spring Boot
How Bola Idor Manifests in Spring Boot
BOLA/IdOR (Broken Object Level Authorization/Insecure Direct Object References) occurs when an application exposes direct references to objects and fails to verify whether the current user has permission to access them. In Spring Boot applications, this vulnerability often emerges through several specific patterns.
The most common manifestation appears in Spring Data JPA repositories where methods like findById() are called without proper authorization checks. Consider this vulnerable controller pattern:
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderRepository orderRepository;
@GetMapping("/{id}")
public ResponseEntity<Order> getOrder(@PathVariable Long id) {
// Critical flaw: no authorization check
Order order = orderRepository.findById(id).orElseThrow();
return ResponseEntity.ok(order);
}
}
A malicious user could simply increment the order ID in the URL to access any other user's order data. This pattern is particularly prevalent in Spring Boot because the framework's convention-over-configuration approach makes it easy to write minimal code that works but lacks security.
Another Spring Boot-specific pattern involves using @AuthenticationPrincipal incorrectly or not at all. When developers retrieve the authenticated user but fail to validate ownership:
@GetMapping("/{id}")
public ResponseEntity<Order> getOrder(@PathVariable Long id,
@AuthenticationPrincipal User user) {
Order order = orderRepository.findById(id).orElseThrow();
// Missing: if (!order.getUserId().equals(user.getId())) throw forbidden;
return ResponseEntity.ok(order);
}
Spring Boot's @PreAuthorize and @PostAuthorize annotations provide declarative security, but developers often omit them, relying instead on method-level security that doesn't exist. This creates a false sense of security where the code appears protected but isn't.
REST controllers using @PathVariable for object identifiers without validation represent another critical vector. Spring Boot's automatic JSON deserialization can also introduce BOLA when entities are directly exposed without filtering sensitive fields:
@GetMapping("/{id}")
public Order getOrder(@PathVariable Long id) { // Returns entire entity
return orderRepository.findById(id).orElseThrow();
}
The framework's support for H2 database in development environments exacerbates this issue, as developers test with embedded databases and forget to add authorization when deploying to production with user data.
Spring Boot-Specific Detection
Detecting BOLA/IdOR in Spring Boot applications requires both static analysis and dynamic testing approaches. middleBrick's black-box scanning is particularly effective because it can probe endpoints without requiring source code access.
middleBrick tests for BOLA by systematically modifying object identifiers in API requests and observing whether access controls properly restrict data. For a Spring Boot endpoint like:
@GetMapping("/users/{userId}/orders/{orderId}")
public ResponseEntity<Order> getUserOrder(@PathVariable Long userId,
@PathVariable Long orderId) {
// No authorization check
return ResponseEntity.ok(orderRepository.findById(orderId).orElseThrow());
}
middleBrick would test variations like:
GET /api/users/1/orders/100
GET /api/users/999/orders/100 # Different user ID
GET /api/users/1/orders/999 # Different order ID
The scanner identifies whether changing these parameters returns data from other users' accounts, which would indicate a BOLA vulnerability.
For Spring Boot applications using Spring Security, middleBrick also checks for proper @PreAuthorize usage. It can detect patterns where:
- Authentication is present but authorization is missing
- Method security annotations are commented out or removed
- Repository methods are called without service-layer authorization checks
middleBrick's OpenAPI/Swagger analysis is particularly valuable for Spring Boot apps, as many use SpringDoc to auto-generate API documentation. The scanner cross-references endpoint definitions with actual runtime behavior, identifying mismatches between documented security requirements and implemented access controls.
Developers can run middleBrick from the CLI to scan their Spring Boot APIs:
middlebrick scan https://yourapp.com/api
The tool provides a security score with specific findings about BOLA vulnerabilities, including the exact request patterns that triggered the issue and severity ratings based on the sensitivity of exposed data.
Spring Boot-Specific Remediation
Remediating BOLA in Spring Boot requires a defense-in-depth approach using the framework's built-in security features. The most effective pattern is implementing service-layer authorization with Spring Security's method security:
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@PreAuthorize("#order.userId == authentication.principal.id")
public Order getOrder(Long orderId, Long userId) {
return orderRepository.findByIdAndUserId(orderId, userId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
}
}
This ensures the database query itself filters by user ID, preventing unauthorized data retrieval even if authorization checks are bypassed elsewhere.
For repository-level protection, Spring Data JPA provides query methods that include user validation:
public interface OrderRepository extends JpaRepository<Order, Long> {
Optional<Order> findByIdAndUserId(Long id, Long userId);
boolean existsByIdAndUserId(Long id, Long userId);
}
The controller then becomes:
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/{id}")
public ResponseEntity<Order> getOrder(@PathVariable Long id,
@AuthenticationPrincipal User user) {
Order order = orderService.getOrder(id, user.getId());
return ResponseEntity.ok(order);
}
}
Spring Boot's @JsonView annotation helps prevent data exposure by controlling which fields are serialized based on the user's role:
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
@Entity
public class Order {
@JsonView(Views.Public.class)
private Long id;
@JsonView(Views.Public.class)
private String status;
@JsonView(Views.Internal.class)
private BigDecimal totalAmount;
@JsonView(Views.Internal.class)
private String customerNotes;
}
@RestController
public class OrderController {
@GetMapping("/{id}")
@JsonView(Views.Public.class)
public Order getOrder(@AuthenticationPrincipal User user,
@PathVariable Long id) {
// Authorization logic here
return orderRepository.findById(id).orElseThrow();
}
}
For comprehensive protection, Spring Boot developers should implement a custom permission evaluator:
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Autowired
private OrderRepository orderRepository;
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject,
Object permission) {
if (targetDomainObject instanceof Order) {
Order order = (Order) targetDomainObject;
User user = (User) authentication.getPrincipal();
return order.getUserId().equals(user.getId());
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId,
String targetType, Object permission) {
if ("Order".equals(targetType)) {
Order order = orderRepository.findById((Long) targetId).orElse(null);
return hasPermission(authentication, order, permission);
}
return false;
}
}
This evaluator can be registered in the security configuration and used with @PreAuthorize annotations for fine-grained control over object access permissions.
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 |