Clickjacking in Restify with Dynamodb
Clickjacking in Restify with Dynamodb — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side UI redress attack where an attacker tricks a user into interacting with invisible or disguised controls. In a Restify service that uses Dynamodb as a backend data store, the risk emerges at the intersection of dynamic HTML rendering, API endpoints, and data retrieved from Dynamodb. If a Restify route serves HTML or JSON that is later rendered in a frame or embedded context without anti-clickjacking protections, an attacker can embed the endpoint inside an invisible iframe and capture user actions.
Consider a Restify endpoint that fetches user profile data from Dynamodb and returns it as part of a page that may be loaded in third-party sites:
const restify = require('restify');
const AWS = require('aws-sdk');
const dynamo = new AWS.DynamoDB.DocumentClient();
const server = restify.createServer();
server.get('/profile', async (req, res, next) => {
const params = {
TableName: 'users',
Key: { userId: req.query.userId }
};
const data = await dynamo.get(params).promise();
if (!data.Item) { return res.send(404); }
// Danger: embedding user-controlled fields without sanitization or CSP
res.setHeader('Content-Security-Policy', "default-src 'self'"); // missing frame-ancestors
res.send(`
<h1>${data.Item.displayName}</h1>
<p>Email: ${data.Item.email}</p>
`);
return next();
});
server.listen(8080);
Two issues specific to the Restify + Dynamodb combination contribute to clickjacking exposure:
- Dynamodb-stored content (e.g., displayName, email) is rendered directly into HTML without context-aware escaping. If an attacker can influence what is stored in Dynamodb (e.g., via another injection vector), the rendered page becomes a vector for clickjacking when embedded elsewhere.
- The endpoint lacks explicit frame-ancestors enforcement. Browsers that do not receive a Content-Security-Policy frame-ancestors directive may default to allowing the page to be framed, enabling an attacker to overlay invisible controls or UI elements on top of the Restify-served page.
Moreover, if the Restify service exposes JSON endpoints consumed by client-side JavaScript that builds UI, an attacker can embed those endpoints via iframe src and capture clicks intended for legitimate controls. This is especially impactful when the Dynamodb-backed API does not enforce strict referrer or origin checks, allowing the embedded request to succeed and return data that the attacker can visually abuse.
Even unauthenticated endpoints are at risk: an attacker does not need stolen credentials to embed a public Restify + Dynamodb page inside a malicious page. The primary goal is to manipulate the user into performing actions (e.g., changing settings, toggling UI states) while believing they are interacting with the attacker’s page.
Dynamodb-Specific Remediation in Restify — concrete code fixes
To mitigate clickjacking in a Restify service backed by Dynamodb, implement a defense-in-depth strategy that combines CSP framing rules, secure rendering practices, and data hygiene when storing and retrieving from Dynamodb.
1. Enforce a strict Content-Security-Policy frame-ancestors directive on all responses served by Restify, including those that render Dynamodb data. This prevents the page from being embedded anywhere except your own origins:
server.use((req, res, next) => {
res.setHeader('Content-Security-Policy', "default-src 'self'; frame-ancestors 'none'");
return next();
});
2. Context-aware escaping of Dynamodb fields before HTML insertion. Use a library such as escape-html and ensure dynamic values are escaped based on context (HTML body, attribute, or JavaScript):
const escapeHtml = require('escape-html');
server.get('/profile', async (req, res, next) => {
const params = {
TableName: 'users',
Key: { userId: req.query.userId }
};
const data = await dynamo.get(params).promise();
if (!data.Item) { return res.send(404); }
// Safe: escape dynamic content
res.send(`
<h1>${escapeHtml(data.Item.displayName)}</h1>
<p>Email: ${escapeHtml(data.Item.email)}</p>
`);
return next();
});
3. Validate and sanitize any user-supplied input that may affect Dynamodb queries or stored content. For example, if you allow users to update display names, reject or encode values that could be used for UI injection.
4. Add X-Frame-Options as a legacy safeguard for older browsers, while keeping CSP frame-ancestors as the primary control:
server.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
return next();
});
5. When serving JSON to client-side frameworks, ensure your API does not rely solely on embedding responses in iframes. If embedding is necessary, require a strict Origin check on the server side to reject unexpected referrers:
server.use((req, res, next) => {
const allowedOrigin = 'https://app.yourdomain.com';
const origin = req.headers.origin;
if (origin !== allowedOrigin) {
return res.send(403);
}
return next();
});
These steps reduce the clickjacking surface when using Restify with Dynamodb by removing framing opportunities and ensuring stored data cannot be leveraged to render malicious UI.