Prototype Pollution in Buffalo with Hmac Signatures
Prototype Pollution in Buffalo with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Prototype pollution in Buffalo combined with Hmac Signatures can occur when user-controlled data is used to construct objects that later influence property resolution or signature validation. Buffalo applications that dynamically build request contexts or configuration objects may inadvertently allow an attacker to add or modify properties on shared prototypes. When those objects are later used in logic that depends on property values or when Hmac-based integrity checks rely on object shape, the pollution can bypass expected validation or alter runtime behavior.
Consider a scenario where request parameters are merged into a configuration object using a shallow merge. An attacker can supply a payload such as _proto["admin"]=true or constructor.prototype.polluted=true. If Buffalo copies or extends objects using libraries or utilities that do not protect against __proto__, constructor, or prototype keys, these properties can propagate across objects. Later, if Hmac Signatures are computed over a serialized representation of the object, the added properties can change the serialized form, leading to a mismatch or, worse, to acceptance of a tampered payload if the signature is computed over a subset of fields that excludes the polluted property but still affects business logic.
Hmac Signatures are typically generated over a canonical string representation of selected fields (e.g., using a predefined set of headers, query parameters, or body fields). If the application builds the signing input by including properties from a polluted object, the signature may cover unintended data. Conversely, if the signing input excludes certain fields but the application later uses polluted properties to decide which fields to include, an attacker can manipulate which data gets signed. This can lead to signature validation passing for modified requests, enabling privilege escalation or unauthorized actions.
Real-world API security checks in middleBrick identify such risks under BOLA/IDOR and Input Validation categories when object manipulation intersects with integrity checks. For example, an endpoint that accepts JSON payloads and computes an Hmac over selected keys may fail to exclude prototype-manipulable keys from the signing base, allowing an attacker to inject or modify keys that affect routing or authorization without invalidating the signature.
Hmac Signatures-Specific Remediation in Buffalo — concrete code fixes
Remediation focuses on preventing prototype pollution at the point of object construction and ensuring Hmac signing operates over a strict, canonical representation that excludes mutable prototype properties.
- Use
Object.create(null)orObject.assign({}, ...)with a safe merge that filters out prototype-level keys. In Buffalo, when merging incoming query or body data into a context object, prefer immutable patterns.
// Unsafe: susceptible to prototype pollution via __proto__, constructor, or prototype
const params = Object.assign({}, rawQuery);
// Safe: create a clean object and explicitly whitelist keys
const safeParams = Object.create(null);
for (const key of Object.keys(rawQuery)) {
if (["action", "id", "timestamp"].includes(key)) {
safeParams[key] = rawQuery[key];
}
}
- When computing Hmac Signatures, serialize only the intended fields using a deterministic canonical form (e.g., sorted keys, no inherited properties). Do not rely on generic object serialization that may include prototype properties.
const crypto = require("crypto");
function canonicalString(data) {
// data is an object with known, expected keys; exclude anything not explicitly allowed
const keys = Object.keys(data).sort();
const parts = keys.map((k) => `${k}=${encodeURIComponent(data[k])}`);
return parts.join("&");
}
function computeHmac(payload, secret) {
const base = canonicalString(payload);
return crypto.createHmac("sha256", secret).update(base).digest("hex");
}
// Example usage: ensure payload contains only allowed fields
const allowed = ["action", "id", "ts"];
const payload = allowed.reduce((obj, k) => {
if (rawQuery[k] !== undefined) obj[k] = rawQuery[k];
return obj;
}, Object.create(null));
const signature = computeHmac(payload, process.env.HMAC_SECRET);
- Validate and sanitize all user-supplied keys before object construction. Reject or ignore keys that match prototype pollution patterns (
__proto__,constructor,prototype).
function sanitize(obj) {
if (obj && typeof obj === "object") {
if (obj.__proto__ !== undefined) delete obj.__proto__;
if (obj.constructor !== undefined) delete obj.constructor;
if (obj.prototype !== undefined) delete obj.prototype;
}
return obj;
}
- For Hmac verification, compare signatures in constant time and ensure the set of signed fields is fixed and documented. Avoid dynamically altering the signed field set based on object properties that could be influenced by user input.
function verifyHmac(payload, receivedSignature, secret) {
const expected = computeHmac(payload, secret);
// constant-time comparison to avoid timing attacks
return crypto.timingSafeEqual(
Buffer.from(expected, "utf8"),
Buffer.from(receivedSignature, "utf8")
);
}