HIGH timing attackfeathersjscockroachdb

Timing Attack in Feathersjs with Cockroachdb

Timing Attack in Feathersjs with Cockroachdb — how this specific combination creates or exposes the vulnerability

A timing attack in a Feathersjs service using Cockroachdb can occur when authentication or data-retrieval logic performs branching based on whether a record exists or whether a supplied identifier matches an owned record. In Feathersjs, a typical find or get handler may first query Cockroachdb for a row, then conditionally proceed based on the presence of that row. If the response time differs depending on whether the record exists or whether the record belongs to the requesting user, an attacker can infer information by measuring round-trip times.

For example, consider a Feathersjs service defined with an authentication layer that loads the user row from Cockroachdb by ID. If the handler uses a conditional that first checks whether a user record exists and then validates ownership, the time taken to hash a password or to perform the lookup can vary subtly based on whether the row is present. When combined with Cockroachdb, which is compatible with PostgreSQL protocol, differences in query plans, index usage, or network latency to the distributed nodes can amplify timing differences. An attacker who can send crafted requests and observe response times may infer whether specific user identifiers exist in the Cockroachdb cluster, or whether a given resource ID is associated with the authenticated subject.

Concrete attack patterns include probing numeric or UUID identifiers sequentially and measuring how long each request takes. If the service performs an indexed lookup on a primary key and then branches on whether the row belongs to the requester, the timing delta between a missing row and an unauthorized row can be measurable, especially under load or across regions. This becomes more pronounced if additional operations such as hashing or token verification are performed only in one branch. In Feathersjs, hooks that modify context or perform early returns can inadvertently introduce these timing differences. Because Cockroachdb implements serializable isolation and distributed transactions, certain query paths may exhibit variable latencies depending on leaseholder location or contention, further contributing to observable timing variance.

To illustrate, a vulnerable Feathersjs hook might look like the following simplified pattern, where the timing of the database query and the conditional check can leak information:

app.service('users').hooks({
  before: {
    async get(context) {
      const { id } = context.params.query;
      const row = await context.app.get('cockroachdb').query('SELECT * FROM users WHERE id = $1', [id]);
      if (!row) {
        // Timing difference when row is absent
        return Promise.reject(new Error('Not found'));
      }
      if (row.userId !== context.params.user.id) {
        // Timing difference when ownership differs
        throw new Error('Forbidden');
      }
      context.result = row;
    }
  }
});

In this pattern, the time to execute the SELECT and the subsequent conditional checks can vary. An attacker who can perform network-level timing measurements and control the supplied ID may deduce whether a given user ID exists and whether it maps to accessible resources. Because the query is executed against Cockroachdb without constant-time safeguards, the branch behavior is exposed. Mitigations must ensure that execution paths and response behaviors remain consistent regardless of existence or ownership, and that database interactions do not introduce measurable timing differences that can be correlated with sensitive conditions.

Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes

Remediation focuses on making data-access patterns independent of existence and ownership outcomes. In Feathersjs, this can be achieved by ensuring that queries always take a constant amount of time and that ownership checks do not introduce early branches that affect timing. Use a single query that joins the target record with the user context and returns a uniform result shape, avoiding conditional early exits based on presence or ownership.

Below is a secure Feathersjs hook that performs a constant-time lookup against Cockroachdb. The query retrieves the row only when it matches both the identifier and the user ownership; otherwise, it returns a deterministic empty result. This ensures that the response path and timing remain consistent regardless of whether the record exists or is owned by another user:

app.service('users').hooks({
  before: {
    async get(context) {
      const { id } = context.params.query;
      const userId = context.params.user && context.params.user.id;
      // Constant-time query: always returns a row or empty result
      const rows = await context.app.get('cockroachdb').query(
        `SELECT data FROM users WHERE id = $1 AND user_id = $2`,
        [id, userId]
      );
      // Uniform handling: no early throw based on existence or ownership
      context.result = rows.length === 1 ? rows[0] : null;
    }
  }
});

If you need to differentiate between "not found" and "forbidden" at a higher layer without leaking timing, perform the distinction after a constant-time fetch by returning a generic error and logging internally for audit purposes, rather than branching on database-level conditions that affect response duration. The above query uses an indexed lookup on (id, user_id), which should be supported by an index on Cockroachdb to keep latency predictable:

-- Recommended index on Cockroachdb for consistent performance
CREATE INDEX idx_users_id_user_id ON users (id, user_id);

For operations that involve updates or deletions, apply the same principle by always executing the statement and inspecting the number of affected rows, rather than branching on existence checks beforehand:

app.service('messages').hooks({
  before: {
    remove(context) {
      const { id } = context.id;
      const userId = context.params.user && context.params.user.id;
      // Execute without early branching
      const result = context.app.get('cockroachdb').query(
        'DELETE FROM messages WHERE id = $1 AND user_id = $2',
        [id, userId]
      );
      // Uniform handling regardless of whether rows existed
      context.isHandled = true;
      return context;
    }
  }
});

Additionally, consider using parameterized queries and prepared statements where supported by your Cockroachdb driver to reduce variability in parsing and planning time. In Feathersjs, ensure that hooks do not perform asynchronous operations that depend on secret-dependent branches. By aligning your data-access patterns with constant-time principles and leveraging Cockroachdb’s indexing and transactional semantics, you can mitigate timing-based inference attacks while preserving the intended authorization semantics.

Frequently Asked Questions

Can a timing attack in Feathersjs with Cockroachdb reveal whether a user record exists?
Yes, if the service branches based on record existence or ownership and the timing of database queries and conditionals varies, an attacker can infer existence by measuring response times. Mitigate by using constant-time query patterns that do not branch on existence or ownership.
What Cockroachdb-specific steps help prevent timing attacks in Feathersjs services?
Create composite indexes on (id, user_id), use a single query that enforces ownership in the WHERE clause, avoid early throws or conditional returns based on query results, and keep response paths uniform so timing does not depend on sensitive conditions.