Insecure Deserialization in Adonisjs with Cockroachdb
Insecure Deserialization in Adonisjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
Insecure deserialization occurs when an application processes untrusted data without sufficient validation, allowing an attacker to manipulate object graphs during (de)serialization. In AdonisJS, this typically surfaces in two patterns: (1) deserializing user-controlled payloads (e.g., from cookies, query parameters, or request bodies) into internal objects, and (2) storing or retrieving application state from CockroachDB in a way that mixes serialized data with database operations. CockroachDB does not inherently introduce a deserialization flaw, but it can amplify risk when serialized blobs (e.g., JSON, MessagePack, or custom formats) are stored and later reconstructed without strict schema and type checks.
Consider an AdonisJS app that stores user session-like data as a serialized JSON blob in a CockroachDB column and later reconstructs it with JSON.parse or a custom reviver that reconstructs functions or class instances. If an attacker can tamper with the blob (e.g., by modifying a cookie or a database row they can influence), they may inject malicious properties or prototype-polluting values. Because CockroachDB is often used in distributed setups with long-lived connections and replication, a poisoned serialized object can persist across nodes and requests, enabling impacts such as privilege escalation or unauthorized state changes when the app deserializes the data in a privileged context.
AdonisJS-specific risk patterns include:
- Using
Serializerutilities to stash data in cookies or cache and then restoring them without schema validation. - Binding request inputs directly to ORM model attributes that are later serialized to CockroachDB and later deserialized without type guards.
- Relying on JavaScript
Functionconstructors oreval-like patterns to interpret stored rules or expressions, which can be chained with malicious payloads exfiltrated via CockroachDB columns.
These patterns map to the OWASP API Security Top 10 (e.g., Broken Object Level Authorization and Unsafe Deserialization) and can be surfaced by middleBrick’s checks for Input Validation, Unsafe Consumption, and Property Authorization when scanning endpoints that interact with CockroachDB.
Cockroachdb-Specific Remediation in Adonisjs — concrete code fixes
Remediation centers on strict validation, avoiding runtime code construction from serialized data, and enforcing schema-aware handling when storing and retrieving data from CockroachDB. Prefer serializing only plain data and validating on deserialization; never reconstruct executable objects from untrusted input.
1. Use Zod or Joi to validate and parse before deserialization
Define a schema for any data you store in CockroachDB and validate on read. This prevents malformed or malicious payloads from being interpreted as application objects.
import { schema } from '@ioc:AdonisJS/Core/Validator'
const userSettingsSchema = schema.object({
theme: schema.string.optional(),
notifications: schema.boolean.optional(),
roles: schema.array().members(schema.string()),
})
export async function getSettings(ctx) {
const row = await ctx.db
.from('user_settings')
.where('user_id', ctx.auth.user?.id)
.limit(1)
.select('settings_blob')
.then(r => r[0])
// Validate raw blob from CockroachDB before use
const parsed = userSettingsSchema.validate(row.settings_blob)
ctx.response.send(parsed)
}
2. Avoid Function/Class reconstruction; prefer plain data
Do not use revivers that reconstruct functions or class prototypes. Store only JSON-compatible data in CockroachDB and map to DTOs after validation.
import { DateTime } from 'luxon'
import { schema } from '@ioc:AdonisJS/Core/Validator'
const profileSchema = schema.object({
displayName: schema.string(),
timezone: schema.string.optional(),
lastLoginAt: schema.string.check((s) => DateTime.fromISO(s).isValid),
})
export async function saveProfile(ctx) {
const body = profileSchema.validate(ctx.request.body())
await ctx.db
.from('profiles')
.where('user_id', ctx.auth.user?.id)
.update({ profile_data: JSON.stringify(body) })
// Later retrieval
const row = await ctx.db.from('profiles').where('user_id', ctx.auth.user?.id).limit(1).select('profile_data').then(r => r[0])
const safe = profileSchema.validate(JSON.parse(row.profile_data))
ctx.response.send(safe)
}
3. Parameterize queries and enforce schema at the DB layer
Use parameterized statements when writing to CockroachDB to avoid injection that could facilitate tampering of serialized fields. Combine with column-level constraints and application-level checks.
import { schema } from '@ioc:AdonisJS/Core/Validator'
const ruleSchema = schema.object({
action: schema.union([schema.literal('allow'), schema.literal('deny')]),
resource: schema.string(),
})
export async function createRule(ctx) {
const rule = ruleSchema.validate(ctx.request.body())
// Parameterized insert into CockroachDB
await ctx.db
.table('rules')
.insert({ user_id: ctx.auth.user?.id, rule_json: JSON.stringify(rule) })
}
export async function listRules(ctx) {
const rows = await ctx.db
.table('rules')
.where('user_id', ctx.auth.user?.id)
.select('rule_json')
const rules = rows.map(r => ruleSchema.validate(JSON.parse(r.rule_json)))
ctx.response.send(rules)
}
4. Rotate and audit serialized formats
If you must store structured blobs, version your schema and reject data that cannot be mapped to the current version. Log and monitor deserialization failures as potential tampering events.
middleBrick can help identify risky patterns by scanning endpoints that perform deserialization against CockroachDB and flagging inputs that feed object reconstruction or unchecked revivers.