Time Of Check Time Of Use in Feathersjs with Cockroachdb
Time Of Check Time Of Use in Feathersjs with Cockroachdb — how this specific combination creates or exposes the ownership vulnerability
Time Of Check Time Of Use (TOCTOU) occurs when the outcome of a security decision depends on the timing between a check and a subsequent use. In a Feathersjs application using Cockroachdb, this commonly arises in multi-step flows where an authorization check runs in one request and a data-modifying action runs in a later request, without re-verifying context at the point of use.
Consider an ownership model where a user can only modify their own profile record. A naive implementation might first fetch the record, verify the user ID matches, then pass the ID to an update method. Because Feathersjs services are often built as a chain of hooks and the database state can change between calls, an attacker can alter the record’s ownership between the check and the use. For example, if user A fetches /profiles/123 and passes the ID to an update in the same logical operation, user B could concurrently change the record’s user_id to their own in Cockroachdb. The check passes, but the use operates on another user’s data, creating an authorization bypass.
With Cockroachdb’s distributed SQL semantics, this risk can be amplified in scenarios involving client-side transactions or multiple service calls. A common pattern is to read a value to compute a derived field, then write it later. If the read is performed in one service call and the write in another (or inside a transaction that doesn’t revalidate ownership), the window for TOCTOU opens. Additionally, if hooks in Feathersjs do not enforce row-level ownership within a single, strongly consistent query, they implicitly rely on timing assumptions that an attacker can exploit.
Real-world attack patterns include manipulating IDs between authorization and mutation steps, exploiting race conditions in distributed transactions, or leveraging delayed writes to escalate privileges. These map to OWASP API Top 10 A01:2023 broken object level authorization and can be surfaced by middleBrick’s BOLA/IDOR checks, which correlate runtime behavior with OpenAPI/Swagger spec definitions, including $ref resolution paths across services.
Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes
To mitigate TOCTOU in Feathersjs with Cockroachdb, enforce authorization and data access within a single, atomic operation. Avoid splitting checks and mutations across separate hooks or service calls unless you embed verification inside the database transaction itself. Use Cockroachdb’s transactional guarantees to re-validate ownership at write time with the same context used for the decision.
Remediation pattern: embed ownership in the query
Instead of reading a record and then updating by ID, include the user identifier directly in the WHERE clause. This ensures Cockroachdb returns no rows if ownership does not match, closing the TOCTOU gap.
// services/profiles/service.js
const { authenticate } = require('@feathersjs/authentication').hooks;
module.exports = {
before: {
all: [authenticate('jwt')],
find: [],
get: [],
create: [validateProfilePayload],
update: [setOwnerFromToken],
patch: [setOwnerFromToken],
remove: []
},
after: {},
error: {},
resultTransformers: {}
};
// Hook: ensure the payload includes the user ID derived from token
function setOwnerFromToken(context) {
return async context => {
const user = context.params.user;
if (user && user.id) {
// For updates/patches, prepend ownership filter to the WHERE clause
if (!context.data.ownerId) {
context.data.ownerId = user.id;
}
// If id is in params (e.g., PATCH /profiles/123), verify consistency
if (context.id && context.id !== 'any') {
context.params.$or = [];
// Ensure the record matches the authenticated user
context.params.$or.push({ id: context.id, ownerId: user.id });
}
}
return context;
};
}
Remediation pattern: transaction with re-validation
When multi-step logic is required, wrap the operations in a Cockroachdb transaction and re-validate ownership at each step. The example below shows a custom hook that uses the service’s model layer to run a transaction, ensuring that the record checked is the record updated.
// hooks/withOwnershipTransaction.js
const { executeTransaction } = require('../models/cockroach');
module.exports = function ownershipTransaction() {
return async context => {
const user = context.params.user;
if (!user || !user.id) {
throw new Error('Unauthorized');
}
const result = await executeTransaction(async client => {
// Step 1: read with ownership embedded
const row = await client.query(
'SELECT id, owner_id FROM profiles WHERE id = $1 AND owner_id = $2 FOR UPDATE',
[context.id, user.id]
);
if (row.rowCount === 0) {
throw new Error('Not found or insufficient permissions');
}
// Step 2: perform the update within the same transaction
const update = await client.query(
'UPDATE profiles SET data = $1 WHERE id = $2 AND owner_id = $3 RETURNING *',
[context.data, context.id, user.id]
);
return update.rows[0];
});
context.result = result;
context.method = 'custom'; // prevent default CRUD output
return context;
};
};
These patterns align with OWASP recommendations by ensuring that authorization decisions are made as close as possible to the data operation. middleBrick’s BOLA/IDOR checks can validate that such controls are present at runtime, and its integration with CI/CD via the GitHub Action can fail builds if insecure patterns are detected. For continuous protection, the Pro plan’s continuous monitoring can alert on anomalous ownership patterns across deployments.