HIGH time of check time of usefeathersjshmac signatures

Time Of Check Time Of Use in Feathersjs with Hmac Signatures

Time Of Check Time Of Use in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Time Of Check Time Of Use (TOCTOU) is a class of race condition where the state of a resource changes between a check and its subsequent use. In FeathersJS applications that use Hmac Signatures for authentication, TOCTOU can occur when the server validates a signature or associated metadata (e.g., timestamp, nonce) and then relies on that validation to authorize a sensitive action, but the underlying data has changed between the check and the use.

Consider a Feathers service that accepts Hmac-signed requests to perform an operation such as updating an email or changing a plan. A typical flawed flow is:

  1. The client sends a request with an Hmac signature computed over a canonical string that includes a timestamp t, a resource ID, and possibly a version or state field.
  2. The server verifies the Hmac to ensure integrity and authenticity, and then checks that t is within an allowed window (e.g., ±5 minutes).
  3. After the timestamp check, the server fetches the current resource from the database and applies the operation.

The vulnerability arises if the resource’s state (e.g., version, ownership, or eligibility) changes between step 2 and step 3. An attacker could race by modifying the resource (e.g., swapping the email or transferring ownership) after the signature verification but before the server applies the action. Because the server already trusted the earlier check, it proceeds with the use, leading to unauthorized modification or privilege escalation.

Hmac Signatures themselves do not prevent TOCTOU; they ensure that the payload has not been altered in transit. However, if the server uses the signature to establish trust and then performs checks that depend on mutable server-side state, the trust boundary does not protect the later use. Common triggers in Feathers include:

  • Using a timestamp check to prevent replay but then proceeding with a state-dependent operation without re-validating the relevant state at the moment of use.
  • Relying on a client-supplied resource identifier that is verified via Hmac but not re-confirmed against the database immediately before the action.
  • Not using a monotonic or tightly bounded version/token that is checked atomically with the operation.

In the context of the 12 security checks run by middleBrick, a BOLA/IDOR or Property Authorization test can surface this class of issue when signed requests are able to act on resources that have changed state between validation and execution. Because middleBrick scans unauthenticated attack surfaces, it can detect endpoints where timing or state inconsistencies are likely to exist, even without access to the internal codebase.

Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes

Remediation centers on ensuring that the checks that establish trust and the actual use of the operation are tightly coupled and re-validate critical state immediately before the action. Below are concrete patterns and code examples for FeathersJS services.

1. Include a monotonic token or version in the signed payload and validate it atomically

Instead of relying solely on a timestamp, include a server-side version or a one-time token in the data that is signed and verified. Before performing the operation, re-fetch the entity and compare the token/version within the same critical section.

// server/services/account/account.class.js
const crypto = require('crypto');

function generateToken() {
  return crypto.randomBytes(16).toString('hex');
}

// Example service hook to validate Hmac and token/version
function validateHmacWithToken(options) {
  return async context => {
    const { userToken, resourceVersion, ...data } = context.data;
    const payload = {
      timestamp: Date.now(),
      resourceId: context.id,
      resourceVersion,
      userToken
    };
    const expected = crypto.createHmac('sha256', options.secret)
      .update(JSON.stringify(payload))
      .digest('hex');
    const received = context.data.hmac;
    if (received !== expected) {
      throw new Error('Invalid Hmac');
    }
    // Re-fetch the current entity to validate token/version atomically
    const current = await context.app.service('accounts').get(context.id);
    if (current.version !== resourceVersion || current.userToken !== userToken) {
      throw new Error('Stale or invalid resource version/token');
    }
    // Proceed safely
    return context;
  };
}

// Hook registration
const accountService = app => new (class AccountService {
  constructor(options) {
    this.app = app;
    this.options = options || {};
  }
  async create(data) {
    // Example: ensure creation uses validated token/version
    const payload = {
      timestamp: Date.now(),
      resourceId: data.id,
      resourceVersion: data.version,
      userToken: data.userToken
    };
    const hmac = crypto.createHmac('sha256', this.options.secret)
      .update(JSON.stringify(payload))
      .digest('hex');
    if (hmac !== data.hmac) {
      throw new Error('Invalid Hmac on create');
    }
    // Persist token/version with the entity
    return { id: data.id, version: data.version, userToken: data.userToken };
  }
});

accountService.prototype.before = {
  create: [validateHmacWithToken({ secret: process.env.HMAC_SECRET })],
  update: [validateHmacWithToken({ secret: process.env.HMAC_SECRET })],
  patch: [validateHmacWithToken({ secret: process.env.HMAC_SECRET })]
};

2. Use server-side stored nonces or short-lived tokens to prevent replay and race conditions

Maintain a server-side cache (e.g., Redis) of recently used nonces or token identifiers. The nonce must be part of the Hmac input and invalidated immediately upon use, before any state change.

// server/hooks/nonce-hmac.js
const crypto = require('crypto');
const nonceCache = new Set(); // In production, use a distributed cache like Redis

function checkNonce(req, res, next) {
  const { nonce, timestamp, hmac } = req.body;
  if (!nonce || !timestamp) {
    return res.status(400).send('Missing nonce or timestamp');
  }
  // Reject replays
  if (nonceCache.has(nonce)) {
    return res.status(409).send('Replay detected');
  }
  const expected = crypto.createHmac('sha256', process.env.HMAC_SECRET)
    .update(nonce + timestamp)
    .digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(expected))) {
    return res.status(401).send('Invalid Hmac');
  }
  // Mark nonce as used before proceeding to avoid TOCTOU
  nonceCache.add(nonce);
  // Set a TTL for the nonce (e.g., 10 minutes)
  setTimeout(() => nonceCache.delete(nonce), 10 * 60 * 1000);
  next();
}

// Usage in a Feathers hook
const hooks = {
  before: {
    all: [],
    find: [],
    get: [checkNonce],
    create: [checkNonce],
    update: [checkNonce],
    patch: [checkNonce],
    remove: [checkNonce]
  }
};
module.exports = hooks;

3. Re-validate ownership and state immediately before the action, even after Hmac verification

Treat Hmac verification as ensuring message integrity, not as a final authorization gate. Always re-read the resource and verify ownership/state immediately before performing writes.

// server/services/todos/todos.class.js
class TodoService {
  async patch(id, data, params) {
    const { userId, completed } = data;
    // Re-fetch the current todo to ensure ownership and state are valid
    const current = await this.Model.findById(id);
    if (!current) {
      throw new Error('Not found');
    }
    // Verify ownership (example attribute) before applying update
    if (current.userId !== userId) {
      throw new Error('Unauthorized');
    }
    // Apply update atomically
    return super.patch(id, data, params);
  }
}

By coupling signature validation with immediate state re-validation, you eliminate the window where the resource can change between check and use. middleBrick can help identify endpoints where such re-validation appears absent by analyzing runtime behavior against the OpenAPI spec and observed requests.

Frequently Asked Questions

Does Hmac Signatures prevent Time Of Check Time Of Use in Feathersjs?
No. Hmac Signatures ensure integrity and authenticity of a request but do not prevent TOCTOU. If the server validates the signature and then performs additional checks against mutable state between verification and use, a race condition can still occur. Mitigation requires re-validating critical state immediately before the action, ideally within the same execution context.
How can I detect TOCTOU risks in Feathersjs APIs using middleBrick?
middleBrick runs automated checks including BOLA/IDOR and Property Authorization that can surface timing and state inconsistency issues. By scanning unauthenticated attack surfaces and comparing runtime behavior to the OpenAPI spec, it can highlight endpoints where signed requests may act on stale or mutable state, indicating potential TOCTOU.