HIGH rate limiting bypassadonisjsfirestore

Rate Limiting Bypass in Adonisjs with Firestore

Rate Limiting Bypass in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability

AdonisJS is a Node.js web framework that typically relies on in-memory or Redis-based rate limiters. When combined with Google Cloud Firestore as the primary data store, misconfigured or absent rate limiting can allow an unauthenticated or low-privilege caller to issue an excessive number of reads or writes to Firestore. This can manifest as a BFLA/Privilege Escalation or a general abuse vector where costly operations are triggered without throttling, potentially leading to inflated costs or noisy tenant activity.

The bypass occurs when rate limiting is applied at a higher layer but not enforced per authenticated context or per Firestore document path. For example, if a route that calls Firestore is protected only by a global middleware that counts requests per IP, an attacker authenticated with a low-cost plan may still be able to iterate over large collections because the limiter does not account for Firestore operation cost or per-user quotas. Additionally, if Firestore security rules rely on request count fields that are not updated transactionally with the rate limiter state, an attacker can read or write beyond intended limits.

In black-box scanning, middleBrick runs 12 security checks in parallel, including Rate Limiting and BFLA/Privilege Escalation. For an AdonisJS + Firestore endpoint, the scanner tests unauthenticated and low-privilege scenarios to detect whether operations such as document reads or batch writes can be performed without effective throttling. The tool also inspects OpenAPI specs when provided, cross-referencing defined paths and methods with runtime behavior to identify gaps between documented limits and actual enforcement.

Concrete risk patterns include:

  • Endpoints that call Firestore in loops or with pagination but lack per-request or per-user counters.
  • Use of generic middleware that counts requests but does not consider Firestore read units or document-path granularity.
  • Security rules that permit broad read access on collections while assuming the application layer will enforce limits, which may be inconsistently applied across services.

An example vulnerable route in AdonisJS might increment a counter in Redis only for successful responses, but if Redis is unavailable or the counter key is shared across users, the limiter can be bypassed. Meanwhile, Firestore usage continues to accumulate, and findings reported by middleBrick will highlight missing per-path rate controls and suggest aligning limiter state with Firestore operations.

Firestore-Specific Remediation in Adonisjs — concrete code fixes

To remediate rate limiting bypasses when using Firestore with AdonisJS, enforce per-user or per-entity limits before any Firestore operation and ensure atomic updates where feasible. Below are concrete patterns and code examples.

1. Per-user rate limiting with AdonisJS middleware

Use AdonisJS middleware to inspect the authenticated user and apply limits specific to that identity. Combine this with a reliable store such as Redis to track counts.

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { RateLimiterRedis } from 'rate-limiter-flexible'
import { Redis } from '@ioc:Adonis/Addons/Redis'

const rateLimiter = new RateLimiterRedis({
  storeClient: Redis.client,
  keyPrefix: 'firestore_limit',
  points: 100, // 100 requests
  duration: 60, // per 60 seconds
})

export default class RateLimitFirestoreMiddleware {
  public async handle({ auth, response }: HttpContextContract, next: () => Promise) {
    if (!auth.user) {
      response.status(401).json({ error: 'Unauthenticated' })
      return
    }
    const userId = auth.user.id
    try {
      await rateLimiter.consume(userId, 1) // 1 point per request
    } catch (rateLimiterRes) {
      response.status(429).json({ error: 'Rate limit exceeded' })
      return
    }
    await next()
  }
}

2. Firestore read/write cost awareness

Track Firestore document-path costs and apply stricter limits for expensive operations such as collection group reads or multi-document writes. Below is an example of a service that checks a simple in-memory map before proceeding; in production, replace with a distributed store aligned with your rate limiter.

import { DateTime } from 'luxon'

interface PathQuota {
  limit: number
  windowMs: number
  timestamps: number[]
}

const quotas = new Map([
  ['users/public', { limit: 60, windowMs: 60_000, timestamps: [] }],
  ['orders/{id}', { limit: 30, windowMs: 60_000, timestamps: [] }],
])

export function allowFirestoreCall(path: string): boolean {
  const quota = quotas.get(path)
  if (!quota) return false
  const now = DateTime.now().toMillis()
  quota.timestamps = quota.timestamps.filter(t => now - t < quota.windowMs)
  if (quota.timestamps.length >= quota.limit) return false
  quota.timestamps.push(now)
  return true
}

3. Secure Firestore rules combined with application-level checks

Firestore security rules should not solely enforce rate limits, but they can complement application controls by rejecting overly large or frequent requests based on request time metadata. Combine rules with per-user identifiers passed from AdonisJS (e.g., user ID) and validate counts in your service before issuing reads/writes.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read: if request.auth != null && request.auth.uid == userId
                 && request.time - request.auth.token.lastRequestTime < duration.value(1, 'minute')
                 && get(/databases/$(database)/documents/rate_metadata/$(request.auth.uid)).data.count < 100;
      allow write: if request.auth != null && request.auth.uid == userId
                   && request.resource.data.keys().size() <= 10;
    }
  }
}

In this rule, lastRequestTime and count should be updated transactionally from AdonisJS to keep Firestore and application state consistent. middleBrick can validate that such controls exist in your OpenAPI spec and that runtime tests confirm they are effective, mapping findings to frameworks such as OWASP API Top 10 and SOC2.

4. Middleware composition and continuous monitoring

Apply the rate limit middleware to all Firestore-related routes and use the Pro plan’s continuous monitoring to track changes over time. The CLI can be integrated into scripts to verify that Firestore endpoints respond appropriately under load, and the GitHub Action can fail builds if a scan detects missing per-path limits.

Related CWEs: resourceConsumption

CWE IDNameSeverity
CWE-400Uncontrolled Resource Consumption HIGH
CWE-770Allocation of Resources Without Limits MEDIUM
CWE-799Improper Control of Interaction Frequency MEDIUM
CWE-835Infinite Loop HIGH
CWE-1050Excessive Platform Resource Consumption MEDIUM

Frequently Asked Questions

Can rate limiting be enforced solely via Firestore security rules?
Firestore rules can complement application-level controls but are not sufficient alone for robust rate limiting because they lack reliable shared counters and fine-grained per-user windows. Use AdonisJS middleware with a distributed store like Redis for enforcement and let rules add contextual constraints.
How does middleBrick detect rate limiting bypasses in AdonisJS with Firestore?
middleBrick runs parallel checks including Rate Limiting and BFLA/IDOR. It tests unauthenticated and low-privilege interactions with Firestore endpoints, inspects OpenAPI specs for path definitions, and compares runtime behavior against declared limits to identify missing or inconsistent throttling.