HIGH brute force attackadonisjsfirestore

Brute Force Attack in Adonisjs with Firestore

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

A brute force attack against an AdonisJS application using Firestore as the backend typically targets the authentication endpoint. When login routes are not protected by effective rate limiting or account lockout, an attacker can submit a high volume of password guesses against a specific user or email. Because AdonisJS often exposes a REST or JSON API, automated scripts can make thousands of requests per minute. Firestore’s default security rules may allow public read or write access to user documents if not explicitly restricted, which can enable an attacker to enumerate valid user identifiers (e.g., by observing differences between “user not found” and “incorrect password”). This information asymmetry reduces the effort required to guess credentials. Even when Firestore rules restrict access, misconfigured rules might permit unauthenticated queries on the users collection, exposing metadata that aids enumeration. In server-side sessions or token-based flows, weak session storage or predictable tokens can further weaken the overall posture. The combination of AdonisJS application logic and Firestore as the data store can inadvertently amplify brute force risk when protections such as rate limiting, account lockout, and secure rule configuration are incomplete.

Firestore-Specific Remediation in Adonisjs — concrete code fixes

To mitigate brute force risks, combine AdonisJS application-level protections with secure Firestore rules and data modeling. Implement rate limiting on authentication routes using AdonisJS middleware. For example, in start/hooks.ts, register a rate limiter hook:

import { Exception } from '@poppinss/utils'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export const rateLimiter = {
  async handle({ request, auth, response }: HttpContextContract, next: () => Promise) {
    const email = request.input('email')
    const key = `login_attempts:${email}`
    const count = await use('redis').get(key)
    if (count && Number(count) >= 10) {
      throw new Exception('Too many attempts, try again later', 429, 'E_TOO_MANY_REQUESTS')
    }
    await next()
    // increment on success only
    if (/* login success */) {
      await use('redis').set(key, 0, { expiresIn: '1h' })
    } else {
      await use('redis').increment(key, 1, { expiresIn: '1h' })
    }
  },
}

Secure your Firestore rules to prevent public enumeration and enforce per-user limits. Require authentication for user document access and avoid broad allow-read rules. Example Firestore rules to restrict user document reads to the owner and admins:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
      // Optionally allow admin reads via custom claims
      allow read: if request.auth != null && get(/databases/$(database)/documents/admins/$(request.auth.uid)).exists;
    }
    // Deny public enumeration by preventing list operations on users
    match /users/{userId=**} {
      allow list: if false;
    }
  }
}

In AdonisJS, structure your login function to use consistent timing and responses to avoid user enumeration. Use the Firestore SDK safely with hashed lookups. Example login handler in a controller:

import { schema } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { Firestore, getFirestore } from '@google-cloud/firestore'
import { compareSync } from 'bcryptjs'

export default class AuthController {
  private db: Firestore

  constructor() {
    this.db = getFirestore()
  }

  public async login({ request, auth, response }: HttpContextContract) {
    const payload = request.validate({
      schema: schema.create({
        email: schema.string({ trim: true, normalize: true }),
        password: schema.string.optional(),
      }),
    })

    // Use a stable delay to reduce timing differences
    await new Promise((r) => setTimeout(r, 300))

    const userDoc = await this.db.collection('users').where('email', '==', payload.email).limit(1).get()
    const user = userDoc.empty ? null : userDoc.docs[0].data()

    // Always run hash comparison to avoid timing leaks
    const hashed = user ? user.passwordHash : '$2a$10$XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
    const valid = compareSync(payload.password, hashed)

    if (!valid) {
      return response.badRequest({ message: 'Invalid credentials' })
    }

    // Issue token or session
    const token = auth.use('api').generate(user)
    return response.ok({ token })
  }
}

Additionally, monitor authentication endpoints via the middleBrick dashboard or CLI to detect abnormal request patterns. The Pro plan supports continuous monitoring and can integrate with GitHub Action to fail builds if risk scores degrade, helping you catch insecure configurations before deployment. These steps reduce brute force success by increasing attacker effort and limiting useful feedback from the API.

Frequently Asked Questions

Can middleBrick detect brute force patterns in my API logs?
middleBrick scans the unauthenticated attack surface and returns a security risk score with findings such as weak rate limiting. It does not analyze your application logs directly, but its checks help identify endpoints that may be susceptible to brute force attacks.
Does middleBrick provide guidance for securing Firestore rules in AdonisJS?
Yes, findings include prioritized remediation guidance. You can use the CLI (middlebrick scan ) or the Web Dashboard to review rule-level recommendations and track scores over time.