Beast Attack in Feathersjs with Cockroachdb
Beast Attack in Feathersjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
A Beast Attack (a form of BOLA/IDOR) in a Feathersjs application backed by Cockroachdb can occur when object-level authorization is missing or inconsistently applied. Feathersjs services are convention-driven and, if you rely only on the framework’s CRUD hooks without adding explicit record ownership checks, an attacker can manipulate record identifiers to access or modify other users’ data. Cockroachdb, while a robust distributed SQL database, does not enforce application-level authorization; it enforces SQL constraints and permissions. If your Feathersjs service issues queries like SELECT * FROM tickets WHERE id = $1 using a user-provided id without verifying that the ticket belongs to the requesting user, the database will return the record if it exists and the connection’s role has sufficient privilege.
In a typical Feathersjs setup with the @feathersjs/sequelize or a custom adapter, a service might look like:
const { Service } = require('feathers-sequelize');
class TicketService extends Service {
async get(id, params) {
// Risk: No check that the ticket belongs to the requesting user
return super.get(id, params);
}
}
An attacker who knows or guesses another user’s ticket ID can call this endpoint and read or act on records they should not see. If your Cockroachdb connection uses a pooled or service account with broad SELECT rights, the database will serve the data, and Feathersjs will return it as a valid response. This becomes more impactful when combined with other issues such as insufficient rate limiting or missing property-level authorization, enabling enumeration or privilege escalation across records. Because Cockroachdb supports complex queries and secondary indexes, an attacker might also attempt injection via crafted parameters if input validation is weak, though the primary risk here is missing ownership checks rather than SQL injection.
Additional exposure can arise from how Feathersjs handles hooks and the data returned to the client. For example, a before hook that mutates query conditions based on roles must be carefully written; if it fails to restrict the query to the user’s scope, the service may unintentionally expose multiple records. Consider a hook intended to filter by user ID:
app.service('tickets').hooks({
before: {
find(context) {
if (context.params.user) {
context.params.query.userId = context.params.user.id;
}
},
get(context) {
context.params.query.id = context.id;
// Missing verification that the record’s userId matches context.params.user.id
}
}
});
If the get hook does not enforce that the record’s userId equals the authenticated user’s ID, the Beast Attack surface remains open. Cockroachdb will still return the requested row if the ID exists, and Feathersjs will deliver it, assuming the service and adapter do not add further checks. This is why runtime authorization—verifying ownership on every request—is essential when using Feathersjs with Cockroachdb.
Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes
To mitigate Beast Attack risks in Feathersjs with Cockroachdb, enforce record ownership at the service layer and ensure queries are scoped to the authenticated user. The most reliable approach is to apply user scoping in hooks and avoid exposing raw IDs that can be tampered with. Below are concrete code examples that demonstrate secure patterns.
1. Add ownership checks in a custom get hook:
app.service('tickets').hooks({
before: {
async get(context) {
const { user } = context.params;
if (!user) {
throw new Error('Unauthenticated');
}
// Fetch the record with an owner check
const record = await context.app.service('tickets').Model.findOne({
where: { id: context.id, userId: user.id }
});
if (!record) {
throw new Error('Not found');
}
// Optionally attach the record to context for downstream use
context.record = record;
// Prevent the default adapter from running an extra query
context.params.$skipSuper = true;
return record;
}
}
});
2. Scope find queries so that list operations only return records belonging to the user (or allowed scopes):
app.service('tickets').hooks({
before: {
find(context) {
const { user } = context.params;
if (user) {
// Ensure the query is scoped to the user
context.params.query.userId = user.id;
// Optionally remove userId from query if it was provided maliciously
delete context.params.query._userIdOverride;
}
}
});
3. Use Cockroachdb connection roles with least privilege for the application user. For example, create a Cockroachdb user that has SELECT/INSERT/UPDATE/DELETE only on the necessary tables, and avoid using a highly privileged role for routine service operations. This limits the impact of any potential injection or misconfiguration.
4. Validate and sanitize input to prevent secondary attack vectors. Even when ownership is checked, ensure IDs are integers or UUIDs as expected:
const { sanitizeEntity } = require('feathers-commons');
app.service('tickets').hooks({
before: {
async create(context) {
const { user } = context.params;
const data = context.data;
// Ensure incoming data does not override userId
data.userId = user.id;
// Further validation can be applied here
return data;
}
}
});
These patterns ensure that Feathersjs services properly scope requests to the authenticated user’s records when using Cockroachdb, reducing the risk of Beast Attack (BOLA/IDOR) in this specific stack.