Padding Oracle in Adonisjs with Cockroachdb
Padding Oracle in Adonisjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
A padding oracle attack can occur in an Adonisjs application that uses Cockroachdb as the underlying database when cryptographic operations (for example, AES-CBC or other block cipher modes requiring padding) are implemented without proper integrity protection. In this stack, if the application decrypts user-supplied ciphertext and relies on timing differences or error messages to infer whether padding is valid, an attacker can iteratively submit manipulated ciphertexts and observe responses to gradually recover plaintext.
With Cockroachdb, the database itself does not introduce padding oracle issues, but the way Adonisjs handles data before storage and after retrieval can create the conditions for an oracle. For example, if an endpoint accepts an encrypted payload in a request, decrypts it using a secret key, and then queries Cockroachdb based on decrypted fields (such as a user identifier or a token), the application may leak information through inconsistent error handling or different response times for valid versus invalid padding. This is especially relevant when the decrypted data is used in SQL queries that interact with Cockroachdb, because error messages from the ORM or database driver might vary depending on whether decryption succeeded early or failed later due to invalid padding.
Consider an authentication flow where a JSON Web Encryption (JWE) token is decrypted in Adonisjs, and the resulting payload is used to look up a user in Cockroachdb via an ORM like Lucid. If the decryption routine does not use authenticated encryption (e.g., AES-GCM) and instead uses AES-CBC with custom error handling, an attacker who can observe HTTP status codes, response bodies, or timing differences can treat the endpoint as a padding oracle. The attacker does not need database access; they only need the public API endpoint that processes the ciphertext. The Cockroachdb interaction becomes a side channel because the application may behave differently when the decrypted data matches expected schema constraints (e.g., a missing row versus malformed input), thereby revealing information about the padding validity indirectly.
Another scenario involves file or message storage where encrypted blobs are stored in Cockroachdb as bytea or text columns. When retrieving and decrypting these blobs in Adonisjs, inconsistent handling of padding errors versus business logic errors can expose an oracle. For instance, returning a 400 for invalid padding and a 404 for missing records gives an attacker a measurable difference. Even if the application attempts to normalize responses, stack traces or debug logs that include SQL query details can inadvertently disclose padding-related failures when interacting with Cockroachdb, especially if the ORM logs or throws exceptions on malformed data.
To mitigate this specific combination, Adonisjs developers should avoid using raw ciphertext to drive database queries, ensure that decryption uses authenticated encryption with associated data (AEAD) such as AES-GCM, and apply constant-time validation and error handling regardless of padding correctness. Input validation should happen before any database interaction with Cockroachdb, and responses should be uniform for all failure cases to eliminate timing and information leaks. Security scans using middleBrick can detect unauthenticated endpoints that process encrypted payloads and highlight the absence of authenticated encryption or inconsistent error handling patterns that could enable padding oracle attacks in this technology stack.
Cockroachdb-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on using authenticated encryption, avoiding decryption-dependent database queries, and ensuring uniform error handling. Below are concrete Adonisjs code examples that interact with Cockroachdb using the Lucid ORM, demonstrating secure patterns.
1. Use AES-GCM instead of AES-CBC for encryption and decryption. AES-GCM provides confidentiality and integrity, eliminating padding entirely. Store and retrieve encrypted data in Cockroachdb using the bytea type.
import { Encrypt, Decrypt } from '@ioc:Adonisjs/Encryption'
// Encrypt with AES-GCM
const encrypted = Encrypt.encrypt('secret-message', {
algorithm: 'aes-256-gcm',
// Nonce and tag are handled internally and prepended by Adonisjs
})
// Store encrypted in Cockroachdb via Lucid model
await UserQuery.query().insert({
encrypted_payload: encrypted, // column type bytea
})
// Decrypt only after verifying integrity; throws on tampering
const payload = Encrypt.decrypt(row.encrypted_payload)
2. Avoid using decrypted data to build Cockroachdb queries. Instead, store a deterministic, authenticated identifier (e.g., a UUID) alongside the encrypted blob, and query by that identifier using constant-time comparison.
import { v4 as uuidv4 } from 'uuid'
// When storing
const publicId = uuidv4()
await UserQuery.query().insert({
public_id: publicId,
encrypted_payload: Encrypt.encrypt(data),
})
// When retrieving, use public_id directly — no decryption needed for lookup
const user = await UserQuery.query()
.where('public_id', requestedPublicId)
.preload('encryptedPayload') // if related
.first()
if (!user) {
// Return uniform response
throw new ResponseException('Not found', 'NOT_FOUND', 404)
}
3. Normalize error handling and avoid leaking padding or decryption details. Ensure that all failure paths return the same HTTP status and message structure when interacting with Cockroachdb.
try {
const payload = Encrypt.decrypt(ciphertext)
const user = await UserQuery.query()
.where('id', payload.userId)
.limit(1)
.first()
if (!user) {
// Do not indicate whether decryption was the issue
throw new ResponseException('Not found', 'NOT_FOUND', 404)
}
return user
} catch (error) {
// Log internally for diagnostics, but return generic response
logger.error('secure_handling_error', { err: error })
throw new ResponseException('Bad request', 'BAD_REQUEST', 400)
}
4. If you must handle legacy ciphertext that uses CBC, employ a constant-time padding validation routine and do not branch logic on padding validity. Instead, proceed only when authentication tags or HMAC verify successfully before any Cockroachdb operation.
import { timingSafeEqual } from 'crypto'
function verifyPaddingConstantTime(buffer: Uint8Array): boolean {
// Example: verify last byte as padding length without early exit
const padLen = buffer[buffer.length - 1]
if (padLen < 1 || padLen > 16) return false
// Constant-time checks for expected padding bytes would go here
// In practice, prefer AEAD to avoid manual padding handling
return true
}
By combining AEAD encryption, deterministic identifiers for Cockroachdb lookups, and uniform error handling, the Adonisjs application removes the conditions that would allow a padding oracle to exist in this specific stack.