Bola Idor in Feathersjs with Cockroachdb
Bola Idor in Feathersjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
BOLA (Broken Level of Authorization) / IDOR occurs when an API exposes one user’s resource through an identifier that should be restricted. In a FeathersJS service backed by CockroachDB, this often arises when route parameters such as :id are directly mapped to database queries without scoping the request to the authenticated subject. FeathersJS itself is framework-agnostic about authentication; it expects an authenticated params.user object when configured with an auth strategy. If that object is not used to constrain queries, an attacker can substitute a numeric or UUID identifier that belongs to another user and read or modify it.
Consider a typical Feathers service definition:
app.use('/messages', createService({
Model: CockroachdbClient.model('messages'),
paginate: { default: 10, max: 50 }
}));
If the service performs a lookup like find({ query: { id: reqId } }) without ensuring the record’s owner matches params.user.id, the unauthenticated or low-privileged attacker can enumerate IDs and access others’ messages. CockroachDB’s SQL semantics can amplify this: a naive WHERE clause on an integer or UUID column without a tenant/user predicate returns data across rows. Common misconfigurations include omitting ownership columns (user_id, tenant_id) from queries or relying solely on application-level filtering after data is retrieved. The vulnerability is not in CockroachDB itself but in how the ORM or query builder constructs statements that lack a user-scoped predicate. Secondary risks appear when IDs are predictable (sequential integers) or when foreign-key relationships are improperly enforced, enabling horizontal traversal across related resources such as profiles, invoices, or shared documents.
Additional exposure can stem from REST endpoints that accept multiple identifiers (e.g., /messages/:messageId/replies/:replyId). If each segment is validated independently rather than as a graph owned by the same user, BOLA can chain across resources. In distributed schemas that use secondary indexes or computed columns, ensure that every read and write path embeds the user context to prevent inadvertent cross-user access.
Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on scoping every query to the requesting user (or tenant) derived from params.user. Avoid raw ID passthrough; instead, inject ownership predicates into the query object. Below are concrete examples using a CockroachDB client integrated with FeathersJS, assuming user identification is available after authentication.
1. Service configuration with scoped queries
Define a custom before hook that enriches query parameters with the user context. This hook runs before any find/get/create/update/patch operation.
const { AuthenticationError } = require('@feathersjs/errors');
app.service('messages').hooks({
before: {
async find(context) {
const { user } = context.params;
if (!user) {
throw new AuthenticationError('Authentication required');
}
// Ensure the query is scoped to the user
context.params.query.userId = user.id;
// If using pagination, preserve existing filters
return context;
},
async get(context) {
const { user } = context.params;
if (!user) {
throw new AuthenticationError('Authentication required');
}
// Reconstruct query to include ownership
context.params.query = {
id: context.id,
userId: user.id
};
return context;
},
async create(context) {
// Attach the user id to the data payload
if (context.params.user) {
context.data.userId = context.params.user.id;
}
return context;
},
async update(context) {
if (!context.params.user) {
throw new AuthenticationError('Authentication required');
}
// Ensure updates only affect rows owned by the user
context.params.query = {
id: context.id,
userId: context.params.user.id
};
return context;
},
async patch(context) {
if (!context.params.user) {
throw new AuthenticationError('Authentication required');
}
// Scoped patch: restrict to the user’s record
context.params.query = {
id: context.id,
userId: context.params.user.id
};
return context;
}
}
});
2. Query builder examples with CockroachDB client
When using a raw CockroachDB client (e.g., node-postgres), embed the user ID in the SQL WHERE clause. Avoid string interpolation; use parameterized queries to prevent injection.
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false }
});
// Example within a Feathers service find hook
async function findByUser(userId, pagination = {}) {
const client = await pool.connect();
try {
const { rows } = await client.query(
'SELECT id, content, created_at FROM messages WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3',
[userId, pagination.limit || 10, pagination.skip || 0]
);
return rows;
} finally {
client.release();
}
}
// Example for get by ID with ownership check
async function getMessageById(messageId, userId) {
const client = await pool.connect();
try {
const { rows } = await client.query(
'SELECT id, content, user_id FROM messages WHERE id = $1 AND user_id = $2',
[messageId, userId]
);
if (rows.length === 0) {
throw new NotFound('Message not found or access denied');
}
return rows[0];
} finally {
client.release();
}
}
3. Schema and index considerations
Ensure your CockroachDB schema includes an index on (user_id, id) to make scoped lookups efficient and to prevent full table scans. Enforce foreign-key constraints where applicable to maintain referential integrity, but remember that database constraints complement, not replace, application-level authorization checks.
CREATE TABLE messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
content TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_messages_user_id ON messages (user_id, id);
4. Middleware integration with middleBrick
Use middleBrick to validate that your service endpoints do not leak data across users. Run a scan against your FeathersJS endpoint to detect missing ownership predicates. The middleBrick CLI can be integrated into development workflows:
npx middlebrick scan https://api.example.com
For CI/CD, the GitHub Action can enforce a maximum risk score threshold before merging, ensuring BOLA issues are caught early. The MCP Server allows AI coding assistants to trigger scans directly from the editor when modifying service files.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |
Frequently Asked Questions
How can I confirm my FeathersJS service is properly scoped to prevent IDOR?
params.user is used to constrain reads and writes.