HIGH dictionary attackadonisjsfirestore

Dictionary Attack in Adonisjs with Firestore

Dictionary Attack in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability

A dictionary attack in an AdonisJS application that uses Google Cloud Firestore typically arises from weak authentication endpoints combined with permissive Firestore security rules. AdonisJS, a Node.js web framework, often handles login routes where user-supplied identifiers (email or username) are passed to a controller that queries Firestore to locate the account. If the endpoint does not enforce uniform response behavior and robust rate limiting, an attacker can iteratively submit candidate credentials and infer account existence based on timing differences or distinct HTTP status codes. Firestore indexes fields such as email, so a lookup like where('email', '==', input) can be efficient for an attacker running many requests. Even when the application returns a generic error message, subtle timing deviations or conditional logic (for example, hashing a password only when the document exists) can leak information.

In this stack, the attack surface is expanded by Firestore’s real-time listeners and server-side SDK usage. An attacker may probe for valid user documents through IDOR-related patterns if the request includes a user-supplied UID or document path that is not properly validated. If Firestore rules allow read access based on a document field that maps to the authenticated subject, a dictionary attack can pivot into unauthorized enumeration of other users’ data. Moreover, if the AdonisJS route does not enforce strict input validation, an attacker can submit a batch of crafted payloads that exercise different code paths in the Firestore SDK, increasing the chance of triggering verbose errors or logs that aid further exploitation.

The interplay between framework middleware and Firestore’s permission model is critical. AdonisJS middleware can enforce rate limiting, but if it is applied after the Firestore SDK call or uses a coarse window, the backend may still perform unnecessary work for each probe. Firestore’s native query capabilities mean that an attacker does not need to enumerate entire collections; precise queries by indexed fields reduce noise and speed up the dictionary attempt. Without proper request throttling and consistent response shapes, the scanner and manual attacker can correlate response codes and timing to identify valid accounts, which can then be targeted with credential stuffing or password spraying.

Additionally, if the application uses Firestore authentication integrations (for example, custom tokens or linking identities), a dictionary attack may attempt to leverage weak linkage logic. An attacker can iterate through potential identity provider identifiers or email values to discover associations that exist in Firestore but are not exposed through the UI. The presence of indexes on email, phone, or username fields makes these queries fast, and if the AdonisJS route does not apply uniform hashing or constant-time comparison for password verification, side channels may further assist the attacker. Effective defense requires synchronizing framework-level protections with Firestore rule design to ensure that existence checks do not leak information and that rate limiting is applied early in the request lifecycle.

Firestore-Specific Remediation in Adonisjs — concrete code fixes

Remediation focuses on making authentication behavior independent of account existence and hardening Firestore rules and queries. In AdonisJS, configure your login route to always perform a constant-time response path, even when the user document is not found. Use a fixed-duration hash operation or a dummy lookup to obscure timing differences. Validate and normalize input before using it in a Firestore query, and avoid exposing which field caused a failure. Below is an example of a hardened login handler that uses the official Firestore SDK for Node.js within an AdonisJS controller.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { getFirestore, doc, getDoc } from 'firebase/firestore'
import { hash } from '@ioc:Adonis/Core/Hasher'

export default class AuthController {
  public async login({ request, response }: HttpContextContract) {
    const email = request.input('email', '').toLowerCase().trim()
    const password = request.input('password', '')

    // Always fetch a document, even if email is invalid, to keep timing consistent
    const db = getFirestore()
    const userRef = doc(db, 'users', email) // assuming email is used as document ID
    const userSnap = await getDoc(userRef)

    // Use a constant-time comparison helper to avoid timing leaks
    const passwordMatch = await hash.verify(password, userSnap.exists() ? userSnap.data()?.passwordHash : 'dummy')

    if (userSnap.exists() && passwordMatch) {
      // issue token, set session, etc.
      return response.ok({ ok: true })
    }

    // Return the same shape and status regardless of existence
    return response.status(401).json({ ok: false, message: 'Invalid credentials' })
  }
}

Complement this code with defensive Firestore security rules that avoid leaking existence through read permissions. Instead of allowing broad read access by indexed fields, require that queries be constrained by a non-enumerable identifier when possible and enforce that client queries do not use arbitrary where clauses on sensitive fields. The following rules ensure that documents can only be read when the request is authenticated and the caller’s subject matches the document ID, preventing unauthenticated enumeration via the API.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
      allow list: if false;
    }
  }
}

In addition to code and rule changes, integrate middleware that applies early rate limiting and request validation before any Firestore interaction. Use a fixed window or token bucket strategy that limits requests per identity or IP, and reject malformed inputs with the same status code as valid failures. Monitor query patterns to detect spikes that resemble dictionary attempts, and ensure that logging does not include sensitive values that could aid an attacker. With these measures, the AdonisJS and Firestore stack reduces the information leakage that enables successful dictionary attacks while preserving legitimate access for users.

Frequently Asked Questions

Why does using email as a Firestore document ID increase risk in a dictionary attack?
Using email as a document ID makes existence checks efficient and predictable. An attacker can iterate through guessed emails and confirm account presence by document existence or response timing, so prefer non-enumerable identifiers and enforce strict rate limiting.
Can middleware alone stop dictionary attacks in AdonisJS with Firestore?
Middleware helps by enforcing rate limits and input validation, but it must be combined with consistent response shapes and secure Firestore rules. Relying on middleware alone is insufficient if queries and rules leak information about document existence.