Beast Attack in Adonisjs with Firestore
Beast Attack in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability
A Beast Attack (BOLA/IDOR) against an AdonisJS application using Google Cloud Firestore occurs when object-level authorization is missing or bypassed, allowing one authenticated user to operate on another user’s Firestore documents. Firestore security rules alone are not sufficient if the backend does not re-validate ownership; a compromised or malicious client can manipulate identifiers to access or mutate records that should be restricted. In AdonisJS, this commonly maps to routes like PUT /documents/:id or DELETE /documents/:id where the :id is directly forwarded to Firestore without confirming that the authenticated user has permission for that specific document.
Consider an AdonisJS controller that retrieves a document by ID and applies Firestore SDK methods without ownership checks:
import { DateTime } from 'luxon'
import Document from 'App/Models/Document'
async show ({ params, auth }) {
const document = await Document.findOrFail(params.id)
return document
}
If Document maps directly to a Firestore collection and the route relies only on the provided :id, an attacker can increment or guess IDs to enumerate documents (IDs/ORIDs). Even if Firestore rules limit reads to documents where request.auth != null, they may not enforce user-specific subcollections or document ownership fields, enabling cross-user data access.
The risk is compounded when Firestore rules use wildcard matching or overly permissive conditions, such as allowing read/write if request.auth != null without checking user-specific claim-based identifiers (e.g., request.auth.token.sub). An attacker can leverage this to read, modify, or delete other users’ data, leading to privacy breaches or unauthorized operations. This pattern is classified under OWASP API Top 10 (2023) as Broken Object Level Authorization and aligns with common privilege escalation scenarios when authorization is delegated solely to client-side rules without backend enforcement.
AdonisJS routes often bind parameters directly to service-layer methods, and if those methods do not scope queries by the authenticated user’s identity, the combination creates a Beast Attack surface. For example, a service that builds a Firestore query like where('userId', '==', userId) must ensure userId is derived from the authenticated identity, not from user-supplied input. Failure to do so allows attackers to supply arbitrary user IDs and bypass intended access boundaries.
Firestore-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on enforcing ownership on the backend before issuing any Firestore operation. Always derive identifiers from the authenticated user’s identity and validate that the target document belongs to that user. Do not rely on Firestore security rules alone for object-level authorization within your application logic.
Use AdonisJS authentication to obtain the authenticated user’s UID and scope Firestore queries accordingly. For example, if you store user identifiers in Firestore documents as userId, build queries that explicitly filter by that field using the authenticated UID:
import { DateTime } from 'luxon'
import Document from 'App/Models/Document'
async show ({ params, auth }) {
const userId = auth.user?.uid
if (!userId) {
throw new Error('Unauthenticated')
}
const document = await Document.query()
.where('id', params.id)
.where('userId', userId)
.firstOrFail()
return document
}
When using the Firestore SDK directly, construct queries that include the user identifier as a filter condition and avoid referencing IDs supplied by the client without scoping:
import { getFirestore, doc, getDoc } from 'firebase/firestore'
async function getScopedDocument(userId, documentId) {
const db = getFirestore()
const docRef = doc(db, 'documents', documentId)
const snapshot = await getDoc(docRef)
if (!snapshot.exists()) {
throw new Error('Not found')
}
const data = snapshot.data()
if (data.userId !== userId) {
throw new Error('Forbidden: user mismatch')
}
return { id: snapshot.id, ...data }
}
For list endpoints, apply the same scoping to prevent enumeration across users. Instead of fetching all documents and filtering client-side, push the filter into the query:
async index ({ auth }) {
const userId = auth.user?.uid
if (!userId) {
throw new Error('Unauthenticated')
}
const documents = await Document.query()
.where('userId', userId)
.exec()
return documents
}
When Firestore security rules are used, ensure they complement backend checks rather than replace them. For example, rules can enforce that users can only read/write documents where request.auth.token.sub == request.resource.data.userId, but your AdonisJS code should still perform its own ownership validation to reduce risk from misconfigured rules or authentication bypasses. Combine rule-based constraints with explicit query scoping in your controllers and services to mitigate Beast Attack vectors effectively.