Beast Attack in Hapi with Cockroachdb
Beast Attack in Hapi with Cockroachdb — how this specific combination creates or exposes the vulnerability
A Beast Attack (Blind Expectation Abuse) in the context of Hapi and Cockroachdb arises when an API endpoint accepts user-supplied ordering or pagination parameters that are not validated or sanitized, allowing an attacker to force the server to perform unintended comparisons against values it should not expose. In Hapi, this commonly occurs when route query options like sort or cursor are passed directly to database queries without normalization. Cockroachdb, while PostgreSQL-wire compatible, implements distributed SQL with strict type expectations; if Hapi builds SQL strings or dynamic query objects using unchecked input, an attacker can supply specially crafted values that shift logical expectations, such as making a server-side cursor point to a different record than intended.
For example, consider a Hapi route that lists user events sorted by created_at and uses a cursor-based pagination pattern. If the cursor is taken directly from client input and used in a WHERE clause like created_at > $1, an attacker can submit a value that maps to a different user or time window, causing the server to leak records it should not. This expectation mismatch is the core of a Beast Attack: the server’s logic expects a well-formed, trusted cursor derived from a prior legitimate response, but unsanitized input can create an alternate expectation that exposes data across privilege boundaries.
In a Cockroachdb-backed Hapi service, additional risk appears when schema definitions include composite primary keys or secondary indexes that include tenant or organization identifiers. If the application fails to enforce tenant scoping in the query and relies only on the cursor value, an attacker can supply a crafted cursor that references a row belonging to another tenant. Cockroachdb will still return rows if the index exists and the query does not explicitly filter by tenant, effectively bypassing application-level access controls. This becomes critical in multi-tenant deployments where tenant_id is part of the primary key or index but omitted from runtime filters due to dynamic query construction in Hapi handlers.
Moreover, Hapi’s route configuration may inadvertently encourage unsafe patterns. For instance, using request.query to build dynamic SQL without parameterization allows injection-style manipulations of sort direction or column names, which can be chained with malformed cursor values to produce inconsistent ordering or duplicate reads. In distributed Cockroachdb clusters, such inconsistencies can propagate across nodes, amplifying the exposure window. Although Cockroachdb provides serializable isolation, application-layer logic that misuses cursors or order parameters can still leak data before any database-side anomaly is detectable.
Cockroachdb-Specific Remediation in Hapi — concrete code fixes
Remediation focuses on strict input validation, explicit tenant scoping, and safe query construction within Hapi handlers. Always treat cursor values as opaque tokens rather than raw values, and verify tenant context before constructing queries. Below are concrete, working examples for Hapi with Cockroachdb using the pg client.
1. Validate and decode cursor safely
Decode the cursor from base64 and ensure it contains only expected fields. Reject malformed or unexpected payloads.
const cursor = (request.query.cursor || '').trim();
let decoded = null;
try {
decoded = Buffer.from(cursor, 'base64').toString('utf8');
} catch (err) {
throw Boom.badRequest('Invalid cursor encoding');
}
// Expect format: tenantId:timestamp:id
const parts = decoded.split(':');
if (parts.length !== 3) {
throw Boom.badRequest('Invalid cursor format');
}
const [tenantId, timestamp, id] = parts;
if (!/^[0-9a-f-]{1,36}$/i.test(tenantId) || !/^[0-9]+$/.test(timestamp) || !/^[0-9a-f-]{1,36}$/i.test(id)) {
throw Boom.badRequest('Invalid cursor values');
}
2. Enforce tenant scoping in SQL
Always include tenant_id in WHERE and JOIN conditions, even when using indexed columns. Use parameterized queries to avoid injection and ensure Cockroachdb can use indexes safely.
const query = `
SELECT id, name, created_at, tenant_id
FROM events
WHERE tenant_id = $1
AND (created_at, id) > $2
ORDER BY created_at ASC, id ASC
LIMIT $3;
`;
const values = [tenantId, `${timestamp}:${id}`, 50];
const { rows } = await pool.query(query, values);
return rows;
3. Use schema-aware column whitelisting for sorting
If sorting is required, accept only predefined column names and map them to safe identifiers. Do not interpolate user input directly into SQL.
const allowedSortColumns = { date: 'created_at', name: 'name' };
const sortBy = allowedSortColumns[request.query.sortBy] || 'created_at';
const sortOrder = request.query.sortOrder && request.query.sortOrder.toLowerCase() === 'desc' ? 'DESC' : 'ASC';
const safeQuery = `
SELECT id, created_at
FROM events
WHERE tenant_id = $1
ORDER BY ${sortBy} ${sortOrder}
LIMIT $2;
`;
const { rows } = await pool.query(safeQuery, [tenantId, 50]);
return rows;
4. Return opaque cursors
When returning a next cursor, encode tenant context and ordering metadata so the server can validate it on return. This prevents clients from manipulating expectations across requests.
const nextCursor = Buffer.from(`${tenantId}:${newRecord.created_at}:${newRecord.id}`).toString('base64');
return { data: rows, nextCursor };
5. Use prepared statements and connection pooling
Leverage Cockroachdb’s compatibility with PostgreSQL drivers to ensure efficient and safe execution. Configure Hapi to use a connection pool and always prefer parameterized statements over string concatenation.
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false }
});
// Use pool.query with parameterized values as shown above.