Broken Access Control in Spring Boot with Firestore
Broken Access Control in Spring Boot with Firestore — how this specific combination creates or exposes the vulnerability
Broken Access Control occurs when an application fails to enforce proper authorization checks, allowing one user to access or modify data and functionality belonging to another. Using Spring Boot with Google Cloud Firestore can unintentionally expose this vulnerability when application-level authorization is incomplete or misaligned with Firestore security rules. Firestore has its own security rules that govern read and write access at the database level, but developers sometimes rely solely on those rules while neglecting to enforce authorization in the Spring Boot service layer. When Firestore rules are permissive or when rules are misconfigured for collection group queries, a Spring Boot endpoint that forwards authenticated user input directly to Firestore may expose other users’ documents.
Consider a typical pattern: a Spring Boot REST controller retrieves a document using a user ID supplied from the request. If the controller trusts path variables or query parameters without verifying that the authenticated user is allowed to access that specific document, an attacker can change the user identifier to access another user’s data. This is a classic BOLA/IDOR scenario. Even if Firestore rules restrict reads to documents where userId == request.auth.uid, a missing authorization check in Spring Boot means the server may act as an intermediary that forwards unauthorized requests, effectively bypassing intended restrictions when the rules are not comprehensive or when rule syntax is incorrect.
Additionally, Firestore’s flexible query capabilities can lead to privilege escalation when combined with improper role handling in Spring Boot. For example, if an application assigns roles such as user and admin and stores them in Firestore or in a separate identity provider, but the Spring Boot service does not validate those roles on each sensitive operation, an attacker may manipulate requests to perform admin actions. Insecure default configurations, such as allowing read access to a collection group without explicit constraints, can compound the issue by exposing documents beyond the intended scope. The interaction between Spring Boot’s business logic and Firestore rules must be carefully designed so that both layers enforce authorization consistently, with Spring Boot validating user intent and context, and Firestore rules providing a last-line safety net.
Real-world attack patterns include modifying numeric IDs in URLs, reusing captured tokens from other users, or leveraging weak object ownership checks. These are commonly seen in APIs that expose document endpoints like /users/{userId}/settings without verifying that the authenticated user matches userId. In such cases, an attacker can iterate over possible IDs to enumerate and access other users’ settings. This aligns with OWASP API Top 10:2023 A1 Broken Access Control and can be flagged by middleBrick scans, which test unauthenticated attack surfaces and highlight authorization gaps across the 12 security checks, including BOLA/IDOR and Property Authorization.
Firestore-Specific Remediation in Spring Boot — concrete code fixes
To remediate Broken Access Control when using Spring Boot with Firestore, enforce strict ownership checks in the application layer and validate Firestore security rules as part of your design. Always resolve the authenticated user’s identity from the Spring Security context and use it to scope queries, rather than trusting request-supplied identifiers. Below are concrete code examples that demonstrate a secure pattern for reading and writing user-specific documents.
Secure Document Retrieval with Ownership Validation
Instead of accepting a user ID from the request, derive the document path from the authenticated principal. This prevents IDOR by ensuring users can only access their own data.
@RestController
@RequestMapping("/api/profile")
public class ProfileController {
private final Firestore db;
public ProfileController(Firestore db) {
this.db = db;
}
@GetMapping
public ResponseEntity<DocumentSnapshot> getMyProfile(@AuthenticationPrincipal String userId) {
// userId is derived from the authenticated token, not from request params
DocumentReference docRef = db.collection("users").document(userId);
try {
DocumentSnapshot snapshot = docRef.get().get();
if (snapshot.exists()) {
return ResponseEntity.ok(snapshot);
} else {
return ResponseEntity.notFound().build();
}
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
return ResponseEntity.status(500).build();
}
}
}
Scoped Query with User ID Binding
When listing or querying collections, bind the query to the authenticated user to avoid exposing other users’ documents, even if Firestore rules are misconfigured.
@Service
public class NoteService {
private final Firestore db;
public NoteService(Firestore db) {
this.db = db;
}
public List<Note> listNotesForUser(String userId) throws InterruptedException, ExecutionException {
CollectionReference notes = db.collection("users").document(userId).collection("notes");
QuerySnapshot querySnapshot = notes.get().get();
List<Note> notesList = new ArrayList<>
for (QueryDocumentSnapshot doc : querySnapshot.getDocuments()) {
notesList.add(doc.toObject(Note.class));
}
return notesList;
}
}
Firestore Security Rules as a Safety Net
Complement Spring Boot checks with tight Firestore rules that explicitly scope access to the authenticated user. Avoid broad read or write permissions on collections.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
match /notes/{noteId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
}
Role-Based Authorization with Custom Claims
If your application uses roles, store them in Firestore or use Firebase Auth custom claims, and validate them in Spring Boot before performing privileged operations.
@Service
public class AdminService {
private final Firestore db;
public AdminService(Firestore db) {
this.db = db;
}
public boolean isAdmin(String userId) throws InterruptedException, ExecutionException {
DocumentReference userDoc = db.collection("users").document(userId);
DocumentSnapshot snapshot = userDoc.get().get();
if (snapshot.exists()) {
String role = snapshot.getString("role");
return "admin".equals(role);
}
return false;
}
public void performAdminAction(String userId) {
// Check isAdmin before proceeding
}
}
By combining Spring Security context with tightly scoped Firestore queries and rules, you reduce the risk of Broken Access Control. These practices align with compliance frameworks such as OWASP API Top 10 and can be validated through scans that include the 12 checks provided by middleBrick, such as Authentication, BOLA/IDOR, and Property Authorization. For ongoing assurance, the middleBrick Pro plan offers continuous monitoring and CI/CD integration to fail builds if risk scores drop below your threshold, while the Web Dashboard lets you track security scores over time.