Insecure Deserialization in Restify with Hmac Signatures
Insecure Deserialization in Restify with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Insecure deserialization occurs when an application processes untrusted serialized data without sufficient validation. In a Restify service that uses Hmac Signatures for request authentication, a common pattern is to sign a subset of request metadata (such as a timestamp or a user ID) and include that signature in headers. If the server then deserializes attacker-controlled payloads—such as JSON, XML, or MessagePack that reach business logic or cache layers—without strict type checks and allowlists, an attacker can craft serialized objects that execute code during deserialization.
When Hmac Signatures are used, trust is often placed in the integrity of the signed data rather than in the content of the payload itself. For example, a server might verify the Hmac on a JSON body or on a set of headers to decide the request is authentic, then proceed to deserialize the body with functions like JSON.parse on nested objects or with libraries that traverse object graphs. If the deserialization path supports prototype pollution or gadgets (e.g., via constructors or setters in JavaScript objects), an attacker can embed malicious chains in the payload. Because the Hmac only covers selected fields, an attacker can modify unsigned portions of the payload or inject new properties that the deserialization logic will interpret as functions or classes, leading to remote code execution or logic bypass.
In the context of OWASP API Top 10, this maps to API1:2023 Broken Object Level Authorization when deserialization is used to elevate privileges, and it can expose sensitive data or invoke server-side operations. Real-world gadget chains in JavaScript environments have been observed in the wild via crafted objects that abuse built-in prototypes. Even when Hmac Signatures prevent tampering with specific headers, an unsigned or poorly scoped payload can still be deserialized unsafely. In a black-box scan, middleBrick tests such scenarios by probing endpoints that accept serialized formats and inspecting whether deserialization occurs on unauthenticated attack surfaces, alongside Hmac-based auth checks.
Consider a Restify endpoint that accepts a JSON payload and verifies an Hmac on selected headers but deserializes the full body with standard JSON parsing that traverses nested objects. An attacker could submit a payload containing constructor overrides or special properties that, when traversed, trigger side effects during deserialization. Because the Hmac does not cover the entire object graph or enforce strict schema validation, the server may execute unintended code paths. This illustrates why Hmac Signatures must be paired with strict input validation and a deserialization strategy that avoids automatic traversal of untrusted data.
Hmac Signatures-Specific Remediation in Restify — concrete code fixes
To secure Restify services that use Hmac Signatures, ensure that deserialization never operates on attacker-controlled data without strict schema enforcement and type checking. Prefer validating and extracting only required fields rather than deserializing entire object graphs. Below are concrete, realistic code examples that demonstrate secure patterns.
Example 1: Verify Hmac and parse only expected scalar fields
const crypto = require('crypto');
const restify = require('restify');
function verifyHmac(payloadBody, receivedSignature, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payloadBody);
return crypto.timingSafeEqual(Buffer.from(hmac.digest('hex'), 'hex'), Buffer.from(receivedSignature, 'hex'));
}
const server = restify.createServer();
server.use(restify.plugins.bodyParser({ mapParams: false }));
server.post('/resource', (req, res, next) => {
const signature = req.headers['x-hmac-signature'];
if (!signature) {
return res.send(401, { error: 'Missing signature' });
}
// Use the raw body as a string to verify Hmac to avoid relying on parsed objects
const rawBody = JSON.stringify(req.body);
const secret = process.env.HMAC_SECRET;
if (!verifyHmac(rawBody, signature, secret)) {
return res.send(403, { error: 'Invalid signature' });
}
// Whitelist and extract only expected fields; do not trust the full parsed body
const { amount, currency, orderId } = req.body;
if (typeof amount !== 'number' || typeof currency !== 'string' || typeof orderId !== 'string') {
return res.send(400, { error: 'Invalid field types' });
}
// Proceed with business logic using validated scalars
res.send(200, { status: 'ok' });
return next();
});
server.listen(8080, () => console.log('Server listening on port 8080'));
Example 2: Reject unexpected properties and use schema validation
const Ajv = require('ajv');
const addFormats = require('ajv-formats');
const crypto = require('crypto');
const restify = require('restify');
const ajv = new Ajv();
addFormats(ajv);
const schema = {
type: 'object',
required: ['timestamp', 'action', 'token'],
additionalProperties: false,
properties: {
timestamp: { type: 'integer', minimum: 0 },
action: { type: 'string', enum: ['create', 'update'] },
token: { type: 'string', pattern: '^[a-f0-9]{64}$' }
}
};
const validate = ajv.compile(schema);
function verifyHmac(payloadBody, receivedSignature, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payloadBody);
return crypto.timingSafeEqual(Buffer.from(hmac.digest('hex'), 'hex'), Buffer.from(receivedSignature, 'hex'));
}
const server = restify.createServer();
server.use(restify.plugins.bodyParser({ mapParams: false }));
server.post('/command', (req, res, next) => {
const signature = req.headers['x-hmac-signature'];
if (!signature) {
return res.send(401, { error: 'Missing signature' });
}
const rawBody = JSON.stringify(req.body);
if (!verifyHmac(rawBody, signature, process.env.HMAC_SECRET)) {
return res.send(403, { error: 'Invalid signature' });
}
// Validate structure and reject unknown properties to prevent injection via nested objects
if (!validate(req.body)) {
return res.send(400, { errors: validate.errors });
}
// Safe to use req.body.action and req.body.token
res.send(200, { result: 'accepted' });
return next();
});
server.listen(8080, () => console.log('Server listening on port 8080'));
Operational practices
- Scope Hmac to include the request method and path, and avoid signing only a subset of headers that an attacker can change without detection.
- Treat the parsed object graph as untrusted; validate types and reject additionalProperties where possible.
- In CI/CD, use middleBrick to scan your Restify endpoints; the GitHub Action can fail builds if risk scores exceed your threshold, helping catch insecure deserialization patterns before deployment.
- If you use the Web Dashboard or CLI (middlebrick scan <url>) you can track findings over time and prioritize remediation of high-severity issues.