Insecure Design in Feathersjs with Cockroachdb
Insecure Design in Feathersjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
Insecure design in a Feathersjs service that uses Cockroachdb often arises from missing authorization checks at the data-access layer and unbounded query construction. Feathersjs services are typically defined with a feathers application and a service module that relies on a Feathers adapter such as @feathersjs/adapter-sequelize or a custom Feathers adapter that talks to Cockroachdb via an ORM or query builder. When the service does not enforce record-level ownership or tenant isolation, an attacker can manipulate identifiers to access or modify other users' data.
For example, consider a Feathers service for a multi-tenant application where each record includes a tenant_id. If the service allows queries like /messages?userId=other_user_id and does not scope queries by the authenticated tenant, an unauthenticated or low-privilege user can enumerate or modify data belonging to other tenants. This is a BOLA/IDOR pattern enabled by insecure design choices: trusting client-supplied identifiers without validating them against the requesting actor or tenant context.
Another common insecure design pattern is constructing dynamic SQL or ORM conditions directly from user input without sanitization. In Feathersjs, you might extend a service with a custom before hook that builds a where clause using raw request parameters. If these parameters are merged into the query without validation, an attacker can inject conditions that return unintended rows. Cockroachdb, while PostgreSQL-wire compatible, does not protect against application-layer query manipulation; the database executes what the ORM or query builder sends. Therefore, the vulnerability resides in how the Feathers service builds and executes queries rather than in Cockroachdb itself.
Additionally, missing rate limiting or insufficient validation on endpoints that perform write operations can lead to denial-of-impact scenarios. An attacker could spam creation endpoints, exhausting resources or causing data inconsistency. Because Feathersjs encourages rapid service definition, developers might omit ownership checks or tenant scoping, assuming the database will enforce constraints. However, without explicit row-level security policies enforced at the application level, the design is insecure.
To detect such issues, middleBrick runs checks across multiple categories including BOLA/IDOR, Input Validation, and Property Authorization. These checks compare the runtime behavior of the unauthenticated or low-privilege surface against the OpenAPI specification and observed responses, highlighting endpoints where identifiers are not properly scoped and where data exposure risks exist.
Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on ensuring that every query executed against Cockroachdb is scoped to the requester's context and that input is validated before being used to construct query conditions.
- Use authenticated hooks to inject tenant or user context into queries. In a Feathers before hook, read the authenticated user (e.g., from
params.user) and append a mandatory filter fortenant_idor similar ownership field.
// src/hooks/tenant-scoping.js
module.exports = function tenantScopingHook(options = {}) {
return async context => {
const { user } = context.params;
if (!user || !user.tenant_id) {
throw new Error('Unauthenticated or missing tenant context');
}
// Ensure the query is scoped to the tenant
context.params.query = context.params.query || {};
context.params.query.where = context.params.query.where || {};
context.params.query.where.tenant_id = user.tenant_id;
return context;
};
};
- Apply the hook to your Feathers service and validate incoming query parameters to prevent injection through filter objects.
// src/services/messages/messages.service.js
const { Service } = require('feathers-sequelize');
const tenantScopingHook = require('../../hooks/tenant-scoping');
const { validator } = require('feathers-sequelize/lib/query');
class MessagesService extends Service {
setup(app) {
super.setup(app);
this.hooks({
before: {
all: [tenantScopingHook(), validator()],
find: [validateMessageQuery()]
}
});
}
}
// Custom query validator to ensure only allowed fields are used
function validateMessageQuery() {
const allowedFields = new Set(['$limit', '$skip', 'status', 'createdAt']);
return context => {
const query = context.params.query || {};
if (query.$select) {
query.$select.split(',').forEach(field => {
if (!allowedFields.has(field.trim())) {
throw new Error(`Field ${field} is not allowed`);
}
});
}
// Ensure no raw SQL injection via $where
if (query.$where) {
throw new Error('Raw $where clauses are not permitted');
}
return context;
};
}
- When using raw queries or dynamic WHERE conditions, explicitly map and sanitize inputs instead of concatenating strings. For Cockroachdb, prefer parameterized queries through your ORM to avoid SQL injection risks that insecure design might introduce.
// Example using a parameterized query via knex (common with Feathers adapters)
const knex = require('../../db'); // configured Cockroachdb pool
async function findScopedMessages(params) {
const { tenant_id, status } = params.query;
let query = knex('messages').where('tenant_id', tenant_id);
if (status) {
query = query.andWhere('status', status);
}
const rows = await query;
return rows;
}
- Leverage Cockroachdb’s strengths by ensuring your schema includes constraints (e.g., NOT NULL, foreign keys) and that your Feathers service respects them. Combine application-level scoping with database constraints to reduce the risk of insecure design leading to data leakage.
| Remediation Focus | Implementation Pattern | Why It Addresses Insecure Design |
|---|---|---|
| Tenant scoping | Inject tenant_id from authenticated user into every query | Prevents cross-tenant data access |
| Input validation | Whitelist allowed query fields and reject raw $where | Stops query manipulation attacks |
| Parameterized queries | Use Knex or ORM methods instead of string concatenation | Eliminates SQL injection vectors |