Cross Site Request Forgery in Adonisjs with Firestore
Cross Site Request Forgery in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability
Cross Site Request Forgery (CSRF) in an AdonisJS application that uses Google Firestore as a backend can arise from a mismatch between server-side session protections and Firestore’s request model. AdonisJS includes built-in CSRF protection for form-based submissions and for requests that rely on session-based authentication. When you store user identity in sessions and make Firestore calls from controller methods, CSRF risk centers on actions where state changes (writes, updates, deletes) are performed without verifying the intent of the authenticated user.
Consider an endpoint that deletes a Firestore document using an identifier supplied by the request, such as a DELETE or POST route that calls db.collection('items').doc(id).delete(). If the route does not validate a CSRF token (or a same-site cookie policy is not enforced), an attacker can craft a page that triggers that endpoint on behalf of a logged-in user. Because Firestore rules typically enforce authentication at the connection level (e.g., the user is authenticated via session or token), the request reaches Firestore successfully, and the operation executes under the victim’s permissions.
The exposure is compounded by two realities of this stack: AdonisJS routes often render pages that include Firestore document IDs, and those IDs can be predictable or derivable. If a page includes a form that submits a Firestore document ID without a CSRF token, an attacker can leverage social engineering or malicious sites to induce the victim’s browser to issue state-changing requests. Since Firestore operations are asynchronous and decoupled from the web server’s session layer, developers may mistakenly assume that Firestore rules alone prevent unauthorized actions, while missing the need for CSRF defenses at the API endpoint layer.
In practice, the attack flow is:
- The victim logs into an AdonisJS app that integrates with Firestore, establishing a session.
- The attacker induces the victim to visit a malicious page that issues a forged request to a vulnerable AdonisJS route.
- The AdonisJS route executes Firestore operations using the victim’s authenticated context, leading to unauthorized data modification or deletion.
Because Firestore does not inherently associate requests with CSRF tokens, the responsibility falls to the application to ensure that each state-changing route validates origin and intent. MiddleBrick’s scans highlight such gaps by correlating authentication mechanisms with Firestore-accessible endpoints and flagging missing CSRF checks where session-based auth is used.
Firestore-Specific Remediation in Adonisjs — concrete code fixes
Remediation centers on ensuring that every state-changing route in AdonisJS validates CSRF tokens or uses anti-CSRF patterns, regardless of whether the persistence layer is Firestore. Below are concrete fixes and code examples tailored to this stack.
1. Enable CSRF protection for web routes
AdonisJS provides CSRF protection via the csrf middleware. Ensure routes that perform Firestore writes, updates, or deletes are guarded by this middleware. In start/routes.ts, apply the middleware selectively or globally:
import Route from '@ioc:Adonis/Core/Route'
import Csrf from '@ioc:Adonis/Addons/Csrf'
// Apply CSRF protection to specific routes
Route.post('/items/:id/delete', 'ItemsController.destroy')
.middleware(['csrf'])
// Or apply globally for session authenticated routes
Route.group(() => {
Route.post('/items/:id/delete', 'ItemsController.destroy')
}).middleware(['csrf'])
When using session-based authentication, ensure your session middleware is active and that cookies are configured with SameSite=Strict or Lax to reduce cross-origin request risks.
2. Validate origin and anti-CSRF tokens in controller actions that call Firestore
In your controller, explicitly verify the CSRF token before performing Firestore operations. This is especially important for API endpoints that might be called from non-form contexts (e.g., AJAX forms that include tokens in headers).
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import ItemsService from 'App/Services/ItemsService'
export default class ItemsController {
public async destroy({ request, auth, response }: HttpContextContract) {
// Ensure CSRF validation for non-GET requests
if (request.method() !== 'GET') {
await request.validateCSRF()
}
const id = request.param('id')
const user = auth.getUserOrFail()
// Proceed with Firestore deletion only after CSRF check
const itemsService = new ItemsService()
await itemsService.deleteItem(user.id, id)
return response.ok({ deleted: true })
}
}
3. Firestore service with user-based scoping and secure rules
Implement a service that scopes Firestore operations to the authenticated user and uses Firestore security rules to enforce ownership. This layered approach ensures that even if CSRF is bypassed at the API layer, Firestore rules prevent unauthorized writes.
// App/Services/ItemsService.ts
import { Firestore } from '@google-cloud/firestore'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class ItemsService {
private db: Firestore
constructor() {
this.db = new Firestore()
}
async deleteItem(userId: string, itemId: string): Promise {
const itemRef = this.db.collection('items').doc(itemId)
const itemDoc = await itemRef.get()
if (!itemDoc.exists) {
throw new Error('Item not found')
}
const data = itemDoc.data()
if (data.userId !== userId) {
throw new Error('Unauthorized: user does not own this item')
}
await itemRef.delete()
}
}
Corresponding Firestore security rules should validate ownership on the server side:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /items/{itemId} {
allow read, write: if request.auth != null && request.auth.uid == request.resource.data.userId;
}
}
}
4. Use anti-CSRF tokens in AJAX/fetch requests
If your frontend makes direct or indirect calls to AdonisJS endpoints that trigger Firestore operations, include the CSRF token in headers. AdonisJS expects the token in the XSRF-TOKEN cookie or in a header named X-CSRF-Token.
fetch('/items/123/delete', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfTokenFromCookie() // implement cookie reading securely
}
})
By combining AdonisJS CSRF middleware, per-request validation, user-scoped Firestore logic, and strict security rules, you mitigate CSRF risks while preserving the benefits of Firestore integration.