Bleichenbacher Attack in Adonisjs with Firestore
Bleichenbacher Attack in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability
A Bleichenbacher attack is a cryptographic padding oracle technique originally described against PKCS#1 v1.5–based RSA encryption. In the context of an AdonisJS application that uses Google Cloud Firestore as a backend, the vulnerability arises not from Firestore itself, but from how the application handles authentication tokens or encrypted payloads before interacting with Firestore.
Consider an AdonisJS route that accepts an encrypted identifier, decrypts it using RSA, and then uses the resulting value to look up a document in Firestore. If the application distinguishes between a padding error and a Firestore document-miss—returning different HTTP status codes or messages—an attacker can iteratively query the endpoint, submitting modified ciphertexts and observing responses. Over many requests, the attacker can decrypt the ciphertext without ever having the private key, because the padding oracle tells them whether the decrypted plaintext is valid.
In Firestore, this often maps to an endpoint that accepts an encrypted document ID, performs a lookup like docRef.get(), and returns 404 when the document does not exist. If the server throws a distinct error for decryption failure versus a generic 404 for missing documents, the difference becomes an oracle. An attacker can craft ciphertexts that, upon decryption, produce candidate plaintexts consistent with Firestore document naming constraints (e.g., strings that could be valid IDs). By measuring response differences—timing or status code—the attacker gradually recovers the plaintext identifier and then reads or manipulates the targeted Firestore document.
AdonisJS does not inherently introduce this flaw, but its default error handling and structured logging can inadvertently expose distinctions. For example, if decryption errors are caught and rethrown with stack traces, or if Firestore permission issues yield different messages than padding errors, the API surface becomes enumerable. The combination of a cryptographic operation before a Firestore call, inconsistent error responses, and predictable Firestore document IDs creates a practical Bleichenbacher vector.
To determine whether a scan surface is vulnerable, middleBrick runs unauthenticated checks across 12 security categories including Input Validation and Authentication. It compares responses for subtle differences that could act as an oracle, while mapping findings to real-world attack patterns such as CVE‑2016‑2183 (TLS RSA padding oracle) and the broader OWASP API Top 10 category ‘Cryptographic Failures’. The scanner does not exploit or fix the issue, but highlights where error handling and response uniformity must be improved.
Firestore-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on making error paths indistinguishable and avoiding decryption-based lookups where possible. Use constant-time comparison and uniform responses, and design Firestore interactions to avoid leaking document existence via timing or status codes.
1. Uniform error handling for decryption and Firestore lookups
Ensure that decryption errors and document-not-found scenarios return the same HTTP status and a generic message. In AdonisJS, wrap both steps in a single try block and return a consistent 404-like response.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { InvalidCiphertextException } from '@ioc:App/Exceptions'
import Document from 'App/Models/Document'
export default class DocumentsController {
public async show({ request, response }: HttpContextContract) {
const userCiphertext = request.input('id') // base64-encoded ciphertext
try {
const plaintextId = decryptRsaOaep(userCiphertext) // throws on padding error
const doc = await Document.findBy('firestoreId', plaintextId)
if (!doc) {
// Return generic not-found; do not distinguish between missing doc and bad ciphertext
return response.status(404).json({ error: 'Not found' })
}
return response.json(doc.serialize())
} catch (error) {
// Treat decryption errors and Firestore errors uniformly
return response.status(404).json({ error: 'Not found' })
}
}
}
2. Avoid decryption-oracle patterns; use opaque identifiers
Instead of decrypting an identifier that maps directly to a Firestore document ID, use a random, opaque reference stored as a Firestore field. The client provides the opaque reference, and the server performs a single, constant-time Firestore query.
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Document from 'Opaqueref' // a mapping table in Firestore
export default class DocumentsController {
public async show({ request, response }: HttpContextContract) {
const opaqueRef = request.input('ref') // e.g., a UUID stored in Firestore
const doc = await Document.query()
.where('opaqueRef', opaqueRef)
.first()
if (!doc) {
return response.status(404).json({ error: 'Not found' })
}
return response.json(doc.serialize())
}
}
3. Constant-time comparison and rate limiting
If you must compare decrypted values, use a constant-time utility to prevent timing leaks. Combine this with rate limiting to mitigate iterative queries that enable Bleichenbacher-style oracle attacks.
import { timingSafeEqual } from 'crypto'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
function constantTimeEq(a: string, b: string): boolean {
const bufA = Buffer.from(a)
const bufB = Buffer.from(b)
// timingSafeEqual requires equal-length buffers; pad safely if needed
if (bufA.length !== bufB.length) return false
return timingSafeEqual(bufA, bufB)
}
export default class TokensController {
public async verify({ request, response }: HttpContextContract) {
const token = request.input('token')
const expected = 'expected-value' // retrieved via a separate, non-oracle path
const isValid = constantTimeEq(token, expected)
if (!isValid) {
return response.status(403).json({ error: 'Forbidden' })
}
return response.ok()
}
}
4. MiddleBrick scanning for residual risks
After applying these fixes, use the middleBrick CLI to rescan your endpoint. The middlebrick scan <url> command evaluates Authentication, Input Validation, and other categories, providing a per-category breakdown aligned with OWASP API Top 10 and compliance frameworks. For teams with more coverage needs, the Pro plan enables continuous monitoring and CI/CD integration to prevent regression.