HIGH prototype pollutionfeathersjshmac signatures

Prototype Pollution in Feathersjs with Hmac Signatures

Prototype Pollution in Feathersjs with Hmac Signatures

Prototype pollution in FeathersJS combined with Hmac Signatures can occur when user-controlled data reaches property assignment or merge logic before signature validation is fully enforced. In FeathersJS, services often merge incoming payloads onto target objects or configuration, and if those properties are not strictly validated, an attacker can inject properties like __proto__, constructor.prototype, or nested keys that mutate shared prototypes. When Hmac Signatures are used to verify request integrity, a common pattern is to sign a canonical representation of the payload (e.g., a JSON string sorted keys) and verify the signature before processing. If the signature is verified but the payload is still merged into mutable objects without normalization, an attacker who can influence property names can pollute the prototype chain even though the Hmac Signature itself is valid.

Consider a FeathersJS service that expects a JSON body and an X-Signature header. The server recomputes the Hmac over the request body using a shared secret and compares it to the provided signature. If the comparison is performed on the raw body string, an attacker can send a polymorphic payload such as {"__proto__": {"isAdmin": true}} with a valid Hmac Signature. The server verifies the signature successfully, then merges the body into a data object or configuration via spread or Object.assign, causing the prototype pollution. This can lead to privilege escalation or unexpected behavior across instances that share the polluted prototype. The vulnerability is not in the Hmac algorithm but in the handling of the deserialized object after verification, especially when using frameworks that encourage deep merges or dynamic property assignment.

Real-world attack patterns include exploiting property enumeration guards or overriding methods that are later invoked on instances. For example, if FeathersJS hooks rely on properties like params.account and the polluted prototype provides a falsy or malicious value, the hook logic may be bypassed. Another vector involves nested pollution where keys like constructor.prototype["polluted"] are used if the framework or custom code performs recursive merging. Even with Hmac Signatures ensuring payload integrity, the server must treat the deserialized object as untrusted and avoid merging it into global or shared prototypes. Using strict parsing with libraries that do not allow prototype pollution (e.g., JSON.parse with a reviver that rejects dangerous keys) and validating property paths before assignment are essential mitigations.

In the context of OWASP API Top 10, this maps to Broken Object Level Authorization (BOLA) and Injection risks, since prototype pollution can lead to unauthorized access or logic tampering. middleBrick scans for such issues by correlating OpenAPI/Swagger definitions with runtime behavior across its 12 security checks, including Input Validation and Property Authorization. By combining spec analysis with active testing, the scanner can identify endpoints where Hmac Signatures are used but payload handling remains unsafe.

Hmac Signatures-Specific Remediation in Feathersjs

To remediate prototype pollution when using Hmac Signatures in FeathersJS, ensure that payloads are validated and normalized before any merging or assignment, and that Hmac verification occurs on a canonical representation that excludes mutable pollution vectors. Below are concrete code examples demonstrating secure handling.

Example 1: Hmac verification with strict parsing and whitelisted assignment

const crypto = require('crypto');
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');

const app = express(feathers());
const SHARED_SECRET = process.env.HMAC_SECRET;

function verifyHmac(body, receivedSignature) {
  const hmac = crypto.createHmac('sha256', SHARED_SECRET);
  hmac.update(body); // body should be the raw request body string
  const expected = hmac.digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(receivedSignature));
}

app.use('/secure', (req, res, next) => {
  const signature = req.headers['x-signature'];
  let rawBody = '';
  req.on('data', chunk => { rawBody += chunk; });
  req.on('end', () => {
    if (!signature || !verifyHmac(rawBody, signature)) {
      return res.status(401).send({ error: 'invalid_signature' });
    }
    try {
      // Parse with a reviver that blocks dangerous keys
      const payload = JSON.parse(rawBody, (key, value) => {
        if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
          throw new Error('Invalid property key');
        }
        return value;
      });
      req.rawPayload = rawBody;
      req.safePayload = payload;
      next();
    } catch (error) {
      res.status(400).send({ error: 'invalid_json' });
    }
  });
});

const service = feathers().use('/data', {
  async create(data, params) {
    // data is already validated and free of prototype pollution
    return { id: 1, ...data };
  }
});
app.use('/secure', service);

Example 2: Canonical serialization for Hmac and deep-freeze payload

const crypto = require('crypto');
const cloneDeep = require('lodash.clonedeep');

function canonicalStringify(obj) {
  return JSON.stringify(obj, Object.keys(obj).sort());
}

app.use('/signed', (req, res, next) => {
  let rawBody = '';
  req.on('data', chunk => { rawBody += chunk; });
  req.on('end', () => {
    const signature = req.headers['x-signature'];
    const expected = crypto.createHmac('sha256', SHARED_SECRET)
                           .update(rawBody) // raw canonical string
                           .digest('hex');
    if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
      return res.status(401).send({ error: 'invalid_signature' });
    }
    let payload;
    try {
      payload = JSON.parse(rawBody);
    } catch (e) {
      return res.status(400).send({ error: 'invalid_json' });
    }
    // Reject nested pollution by freezing known dangerous constructors
    const freezeDangerous = (obj) => {
      if (obj && typeof obj === 'object') {
        if ('__proto__' in obj || 'constructor' in obj || 'prototype' in obj) {
          throw new Error('Dangerous keys detected');
        }
        Object.freeze(obj);
        Object.values(obj).forEach(freezeDangerous);
      }
    };
    try {
      freezeDangerous(payload);
    } catch (e) {
      return res.status(400).send({ error: 'pollution_attempt' });
    }
    req.safePayload = cloneDeep(payload);
    next();
  });
});

const secured = feathers().use('/items', {
  async update(id, data, params) {
    // data is frozen and safe
    return Object.assign({}, data);
  }
});
app.use('/items', secured);

Key remediation steps include: (1) verifying the Hmac over the raw request body before deserialization, (2) using a JSON reviver or strict schema validation to reject dangerous keys like __proto__, (3) avoiding merge strategies that rely on spread or Object.assign on user input without prior normalization, and (4) freezing or deep-cloning validated payloads before they enter service logic. These practices ensure Hmac Signatures provide integrity without inadvertently enabling prototype pollution.

middleBrick can help by scanning your OpenAPI spec and runtime behavior to flag endpoints where Hmac verification is present but input validation or property authorization checks are weak, aligning with its checks for Input Validation and Property Authorization.

Frequently Asked Questions

Can a valid Hmac Signature prevent prototype pollution?
No. A valid Hmac Signature ensures payload integrity but does not prevent prototype pollution if the server merges or assigns deserialized properties without strict validation. Prototype pollution must be mitigated through input normalization and rejection of dangerous keys.
How does middleBrick detect prototype pollution risks with Hmac Signatures?
middleBrick correlates OpenAPI/Swagger definitions with runtime findings across its 12 checks, including Input Validation and Property Authorization. It identifies endpoints that accept user-controlled data and flags weak handling after Hmac verification where pollution risks exist.