HIGH replay attackfeathersjsapi keys

Replay Attack in Feathersjs with Api Keys

Replay Attack in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability

A replay attack occurs when an attacker intercepts a valid request and retransmits it to reproduce the intended effect without going through the authentication or authorization flow. In FeathersJS, using API keys over HTTPS without additional protections can expose endpoints to this class of issue. If API key transmission is done in a static header and the service does not prevent request replay, an attacker who observes a valid keyed request (for example by sniffing network traffic or via a compromised log) can resend it to the same endpoint. Because the API key proves possession of a shared secret but does not change per request, the server may accept the duplicated request as legitimate.

FeathersJS commonly uses hooks for authentication. When API keys are verified inside a before hook, the hook may simply check the presence and validity of a key and then allow the service method to proceed. Without a nonce, timestamp, or idempotency mechanism, the same keyed request can be replayed with the same parameters. For example, consider a payment or resource creation endpoint that accepts a POST with a stable payload; replaying that request could cause duplicate charges or state changes. The risk is higher when the API key is long-lived and reused across many requests, and when transport security is not consistently enforced.

Another scenario specific to Feathers is dynamic service methods that rely on query parameters or body fields that are not tied to per-request uniqueness. If an API key is the only credential and the endpoint does not validate that the request context is fresh, replaying a recorded call can bypass intended checks. MiddleBrick’s scan flags this as an unauthenticated or poorly authenticated attack surface when replay protections such as one-time nonces or short-lived signed tokens are absent. Even when HTTPS is used, replay remains a concern because HTTPS protects confidentiality and integrity in transit, but does not prevent an adversary from retransmitting an already authorized request.

Middleware and hooks in Feathers can unintentionally make replay easier if developers log full requests including headers containing API keys and those logs are exposed. Additionally, if the client embeds the API key in a predictable location (e.g., a static HTTP header) and does not rotate or scope the key per consumer, replay becomes more feasible. The combination of a simple API key mechanism and lack of replay countermeasures aligns with common weaknesses in the OWASP API Security Top 10, particularly insufficient anti-replay controls in authenticated endpoints.

Api Keys-Specific Remediation in Feathersjs — concrete code fixes

To mitigate replay risks while continuing to use API keys in FeathersJS, introduce per-request uniqueness and validation logic in hooks. Common approaches include requiring a client-supplied nonce combined with server-side tracking, or embedding a timestamp and ensuring recentness of requests. Below are concrete examples that you can adapt to your service.

Example 1: Nonce-based protection in a before hook

Maintain a short-lived cache of used nonces (e.g., using an in-memory store or Redis) and reject requests with a repeated nonce within the validity window. This example uses a before hook to enforce uniqueness.

// src/hooks/require-nonce.js
const nonces = new Set();
const NONCE_TTL_MS = 300000; // 5 minutes

function cleanupOldNonces() {
  // In production, use a proper TTL cache; this is illustrative.
}

module.exports = function () {
  return async context => {
    const { headers } = context.params;
    const nonce = headers && headers['x-nonce'];
    const timestamp = headers && headers['x-timestamp'];

    if (!nonce || !timestamp) {
      throw new Error('Missing nonce or timestamp header');
    }

    const now = Date.now();
    const ts = parseInt(timestamp, 10);
    // Reject if timestamp is older than 2 minutes
    if (now - ts > 120000) {
      throw new Error('Request timestamp too old');
    }

    if (nonces.has(nonce)) {
      throw new Error('Replay detected: nonce already used');
    }

    nonces.add(nonce);
    setTimeout(() => nonces.delete(nonce), NONCE_TTL_MS);

    // Proceed to authentication checks or API key validation
    const apiKey = headers['x-api-key'];
    if (!apiKey || apiKey !== process.env.SERVICE_API_KEY) {
      throw new Error('Invalid API key');
    }

    return context;
  };
};

Example 2: Timestamp + HMAC with API key as a signing key

Instead of sending the API key directly in every header, use it to sign a combination of timestamp and method/path. The server recomputes the signature and verifies freshness, which prevents replay without storing nonces.

// Client-side pseudo-code (Node.js)
const crypto = require('crypto');
function signRequest(apiKey, method, path, body, timestamp) {
  const hmac = crypto.createHmac('sha256', apiKey);
  hmac.update([method, path, JSON.stringify(body), timestamp].join('|'));
  return hmac.digest('hex');
}

const apiKey = process.env.API_KEY;
const timestamp = Date.now().toString();
const signature = signRequest(apiKey, 'POST', '/v1/resources', { amount: 100 }, timestamp);

// Headers:
// x-timestamp: timestamp
// x-signature: signature
// x-api-key: apiKey

// Server-side hook (simplified)
module.exports = function () {
  return async context => {
    const { headers, data } = context.params;
    const timestamp = headers['x-timestamp'];
    const receivedSig = headers['x-signature'];
    const apiKey = headers['x-api-key'];

    if (!timestamp || !receivedSig) {
      throw new Error('Missing signature or timestamp');
    }

    const now = Date.now();
    if (Math.abs(now - parseInt(timestamp, 10)) > 120000) {
      throw new Error('Request expired');
    }

    const expectedSig = signRequest(apiKey, context.method, context.path, data, timestamp);
    if (!crypto.timingSafeEqual(Buffer.from(expectedSig), Buffer.from(receivedSig))) {
      throw new Error('Invalid signature');
    }

    // Optionally validate API key usage scope here
    return context;
  };
};

Example 3: MiddleBird integration for ongoing scanning

Use MiddleBird’s scans to detect missing replay protections in your FeathersJS OpenAPI spec and runtime behavior. You can run the CLI to verify your endpoints and then add CI/CD enforcement with the GitHub Action to fail builds if risk scores degrade. The MCP Server allows you to scan directly from your IDE while you develop hooks, ensuring that new endpoints include appropriate freshness checks.

# Scan your Feathers service with the CLI
middlebrick scan https://api.example.com/openapi.json

Frequently Asked Questions

Can API keys alone be sufficient to prevent replay attacks in FeathersJS?
API keys alone are not sufficient to prevent replay attacks. You should combine API keys with per-request nonces, timestamps, or HMAC-based request signing so that each request is unique and time-bound.
How often should I rotate API keys in a FeathersJS service to reduce replay risk?
Rotate keys based on exposure risk and usage patterns. For high-risk operations, consider short-lived keys or key bindings to specific scopes; for lower-risk services, regular rotation (e.g., every 90 days) combined with replay protections is reasonable.