Broken Access Control in Laravel with Firestore
Broken Access Control in Laravel with Firestore — how this specific combination creates or exposes the vulnerability
Broken Access Control occurs when application logic fails to enforce proper authorization on API endpoints or data operations. In a Laravel application that uses Google Cloud Firestore as a backend, this risk arises from a mismatch between Laravel’s authentication/authorization mechanisms and Firestore’s permission model. Developers may assume that server-side Laravel guards (middleware, policies, gates) are sufficient, but if Firestore security rules are not aligned with Laravel’s access checks, unauthenticated or over-privileged application users can directly or indirectly reach Firestore paths they should not.
Consider a multi-tenant SaaS built with Laravel and Firestore where tenant data is segregated by a tenant_id field in each document. If Laravel authorizes a request based on user roles (e.g., is_admin or is_editor) but does not enforce tenant scoping at the Firestore rule level, an attacker who compromises or manipulates their session could iterate over tenant IDs and read or modify documents across tenants. This is a BOLA/IDOR pattern: insecure direct object references to tenant-scoped Firestore documents. Because Firestore rules are evaluated independently of Laravel’s runtime context, missing rule constraints mean Laravel’s authorization is bypassed at the database layer.
The API scanner detects this as a BOLA/IDOR finding and also flags related categories such as Authentication and Property Authorization. Firestore rules that are either too permissive (e.g., allowing read/write if request.auth != null without validating tenant_id alignment) or missing entirely expand the attack surface. Attack patterns include horizontal privilege escalation (a regular user accessing another user’s data) and vertical escalation (a user invoking admin-only operations). The scanner’s parallel checks for BOLA/IDOR, Authentication, and Property Authorization highlight the need to couple Laravel middleware with strict Firestore rules that validate tenant_id, resource ownership, and required claims on each request.
Additionally, when Laravel exposes Firestore operations through custom endpoints or GraphQL-like resolvers, improper input validation can lead to injection or data exposure. For example, concatenating user input into document paths without strict allowlists enables path traversal or unauthorized document access. The scanner’s Input Validation and Data Exposure checks surface these risks by testing unauthenticated endpoints and inspecting whether responses reveal sensitive fields or metadata. Effective protection requires treating Firestore as an untrusted boundary: validate tenant context, user permissions, and document paths in Laravel, and enforce equivalent constraints in Firestore security rules.
Firestore-Specific Remediation in Laravel — concrete code fixes
Remediation centers on aligning Laravel authorization with Firestore security rules and ensuring tenant scoping, ownership checks, and strict input validation. Below are concrete practices and code examples for Laravel with Firestore.
- Enforce tenant scoping in Laravel before Firestore operations. Resolve the tenant from the authenticated user and verify ownership before constructing document references. Example using the Google Cloud Firestore PHP client:
use Google\Cloud\Firestore\FirestoreClient;
use Illuminate\Support\Facades\Auth;
$firestore = new FirestoreClient([
'projectId' => env('GOOGLE_CLOUD_PROJECT'),
]);
$userId = Auth::id();
$tenantId = Auth::user()->tenant_id;
// Validate tenant and fetch user-owned document reference
$docRef = $firestore->collection('tenants')
->document($tenantId)
->collection('items')
->document($itemId);
// Ensure the item belongs to the tenant before read/write
$snapshot = $docRef->snapshot();
if (! $snapshot->exists()) {
abort(404);
}
$documentData = $snapshot->data();
if ($documentData['tenant_id'] !== $tenantId) {
abort(403, 'Forbidden: tenant mismatch');
}
- Define Firestore security rules that mirror tenant and ownership checks. Rules cannot rely on Laravel state, so include tenant_id in each document and validate it in rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /tenants/{tenantId}/items/{itemId} {
allow read, write: if request.auth != null
&& request.auth.token.tenant_id == tenantId
&& request.auth.uid == request.resource.data.owner_uid;
}
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
- Apply Laravel policies or gates that reflect these constraints and pass tenant context to views or API responses. Combine with input validation to ensure only allowlisted document paths are used:
use Illuminate\Support\Facades\Validator;
$validator = Validator::make($request->all(), [
'tenant_id' => 'required|string|exists:tenants,id',
'item_id' => 'required|string',
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
// Proceed only after validation
- For LLM/AI Security considerations: if your Laravel app exposes Firestore operations to AI-assisted tooling or MCP integrations, ensure prompts and tool schemas do not leak tenant identifiers or sensitive document structures. The scanner’s LLM/AI Security checks can detect system prompt leakage and injection attempts targeting such integrations, so audit any AI-assisted endpoints that reference Firestore paths.