Clickjacking in Feathersjs with Hmac Signatures
Clickjacking in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side attack that tricks a user into interacting with a hidden UI element inside an iframe. When FeathersJS services use Hmac Signatures only for request authentication and do not enforce frame-ancestor protections, an attacker can embed the authenticated application in an invisible iframe and drive user actions via social engineering. Because Hmac Signatures typically bind a request to a user session or API key, the forged requests may appear valid to the server, especially if the signature covers method, path, and timestamp but not the execution context (e.g., frame origin).
Consider a FeathersJS API that validates an Hmac Signature header on sensitive mutations such as changing email or elevating permissions. If the response includes cookies without SameSite=Strict or SameSite=Lax, and the page is embeddable in an external site, a malicious page can load the FeathersJS UI or invoke endpoints via a forged form. Even if the Hmac Signature prevents tampering of the payload, the browser will still send cookies (e.g., session identifiers) with the request, allowing the attacker to perform actions on behalf of the authenticated user. This is a classic UI redress issue where the trust model relies on Hmac Signatures for integrity but lacks contextual binding to the rendering origin.
In a typical FeathersJS + Hmac Signatures setup, the server generates a signature over selected headers and the request body, and the client includes that signature in a custom header (for example, x-api-signature). If the client-side JavaScript is served with overly permissive Content-Security-Policy (CSP) that allows framing, an attacker can embed the client page. Because the Hmac Signature does not include an anti-CSRF token tied to the page origin, the server cannot distinguish a legitimate user-initiated action from a forged one initiated via an invisible iframe. The risk is especially pronounced when the API does not validate the Origin or Referer headers in conjunction with the Hmac Signature. OWASP API Top 10 2023 A5 (2021) highlights broken function-level authorization and insufficient user interaction validation; clickjacking in this context shows how authorization mechanisms like Hmac Signatures can coexist with a missing UI-level protection.
To detect this during a scan, middleBrick evaluates whether the application embeds sensitive pages in frames and whether Hmac Signature validation is performed without origin or anti-CSRF safeguards. The tool checks for restrictive CSP frame-ancestors, the presence of SameSite cookies, and whether sensitive endpoints require additional context beyond the Hmac Signature. Without these controls, an authenticated endpoint protected only by Hmac Signatures remains vulnerable to user-mediated actions triggered through embedded content.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on ensuring that Hmac Signatures are used in combination with anti-CSRF tokens and strict CSP, and that sensitive endpoints validate request context. Below are concrete, syntactically correct examples for a FeathersJS service using Hmac Signatures.
1. Enforce SameSite and Secure cookies
Configure your FeathersJS app to set SameSite and Secure flags on cookies, preventing them from being sent in cross-site contexts.
const feathers = require('@feathersjs/feathers');
const express = require('@feathsjs/express');
const app = express(feathers());
app.use('/', express.static('public'));
app.configure(express.rest());
app.configure(express.json());
app.configure(express.urlencoded({ extended: true }));
// Ensure cookies have SameSite and Secure
app.set('cookie options', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict'
});
// Your services and hooks here
module.exports = app;
2. Add an anti-CSRF token and include it in Hmac signing
Generate a per-request or per-session anti-CSRF token and include it in the data used to compute the Hmac Signature. This binds the signature to a specific page origin.
const crypto = require('crypto');
// Example middleware to generate and validate CSRF token
app.hooks({
before: {
create: [context => {
const token = crypto.randomBytes(32).toString('hex');
context.params.csrfToken = token;
// Store token in session or a signed cookie
context.params.session.csrfToken = token;
return context;
}],
all: [context => {
const signature = context.headers['x-api-signature'];
const token = context.params.session.csrfToken;
const payload = context.method + context.path + JSON.stringify(context.data) + token;
const expected = crypto.createHmac('sha256', process.env.HMAC_SECRET)
.update(payload)
.digest('hex');
if (signature !== expected) {
throw new Error('Invalid signature');
}
return context;
}]
}
});
// Example service that requires CSRF token
app.use('/todos', {
async create(data, params) {
if (params.csrfToken !== params.session.csrfToken) {
throw new Error('CSRF token mismatch');
}
// Proceed with creation
return data;
},
options: {
hmacFields: ['method', 'path', 'body', 'csrfToken']
}
});
3. Configure a strict Content-Security-Policy
Set CSP headers to disallow framing by specifying frame-ancestors.
const csp = require('helmet-csp');
app.use(csp({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", 'https://trusted.cdn.com'],
styleSrc: ["'self'", "'unsafe-inline'"],
frameAncestors: ["'none'"]
}
}));
4. Validate Origin/Referer for state-changing requests
In hooks, verify that the request’s Origin or Referer matches your trusted domain when Hmac Signatures are used.
app.hooks({
before: {
create: [context => {
const allowedOrigin = 'https://yourdomain.com';
const origin = context.headers.origin;
const referer = context.headers.referer;
if (origin !== allowedOrigin && referer !== allowedOrigin) {
throw new Error('Invalid request origin');
}
return context;
}]
}
});
5. Example Hmac Signature generation and verification with origin binding
Include the request origin in the signed payload to prevent the request from being honored when embedded in a malicious frame.
const crypto = require('crypto');
function generateHmac(method, path, body, origin, secret) {
const payload = method + '|' + path + '|' + JSON.stringify(body) + '|' + origin;
return crypto.createHmac('sha256', secret).update(payload).digest('hex');
}
// Client-side: include origin in signature
const signature = generateHmac(
'POST',
'/users',
{ email: '[email protected]' },
window.location.origin,
process.env.HMAC_SECRET
);
// Server-side verification in a hook
app.hooks({
before: {
create: [context => {
const expected = generateHmac(
context.method,
context.path,
context.data,
context.headers.origin,
process.env.HMAC_SECRET
);
if (context.headers['x-api-signature'] !== expected) {
throw new Error('Invalid Hmac Signature');
}
return context;
}]
}
});
6. Use framework-level protections where available
If you use additional UI layers (e.g., a frontend framework), ensure that forms include a CSRF token and that the token is part of the signed Hmac payload. middleBrick can help identify whether your endpoints validate origin and CSRF context alongside Hmac Signatures.