Clickjacking in Feathersjs with Dynamodb
Clickjacking in Feathersjs with Dynamodb — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side UI redress attack where an invisible or misleading UI layer tricks a user into interacting with a page they did not intend to interact with. When using FeathersJS with DynamoDB as the data store, the risk does not stem from DynamoDB itself but from how FeathersJS services and pages are rendered and exposed. If a FeathersJS application serves HTML pages (for example via an SSR view engine or static assets) and embeds API endpoints or forms inside iframes, those interfaces can be embedded by an attacker on a malicious site. Because DynamoDB typically stores permissions and role data used by FeathersJS service hooks, an attacker may leverage a compromised session or unauthenticated service endpoint to perform actions on behalf of a victim after clickjacking interactions.
In a FeathersJS + DynamoDB stack, clickjacking exposure often arises at two levels:
- Unauthenticated or weakly authenticated service endpoints: If a FeathersJS service that interacts with DynamoDB does not enforce authentication for certain actions (for example, public read endpoints that also accept state-changing methods due to misconfigured hooks), an attacker can simulate authorized requests after a user is tricked into clicking.
- Overly permissive service hooks and data exposure: DynamoDB data can include sensitive fields (e.g., roles, tokens, or user identifiers). If FeathersJS service hooks or client-side code expose these fields in ways that an attacker can read or act upon via embedded UI, the attack surface widens.
An attacker might craft a page that loads a FeathersJS management page inside a hidden iframe and overlays interactive elements on top, causing a logged-in user to inadvertently change settings or invoke DynamoDB-backed operations. Because FeathersJS can auto-generate REST and WebSocket interfaces from DynamoDB-driven services, ensuring that services are not unintentionally exposed and that rendered UI includes anti-clickjacking protections is critical.
Dynamodb-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on securing FeathersJS services and ensuring that DynamoDB-backed data and operations are not exposed to unauthorized interactions. Apply HTTP response headers and FeathersJS middleware to prevent embedding and enforce secure interactions.
1. Prevent rendering and embedding in iframes
Ensure your FeathersJS server sends headers that prevent clickjacking by disallowing iframe embedding. For Express-based Feathers apps, use middleware before the Feathers initializer.
// src/app.js (Express-based Feathers app)
const feathers = require('@feathersjs/feathers');
const express = require('@feathsjs/express');
const app = express(feathers());
// Clickjacking protection header
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
// Alternatively: SAMEORIGIN if you need limited embedding
next();
});
// Other middleware and Feathers setup...
app.configure(require('@feathersjs/express').rest());
app.configure(require('@feathersjs/socketio'));
module.exports = app;
If you serve frontend assets that interact with FeathersJS services, ensure those assets are not hosted in a way that allows arbitrary embedding. CSP frame-ancestors can provide additional defense:
// Content-Security-Policy frame-ancestors example
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
next();
});
2. Secure DynamoDB-backed FeathersJS services
DynamoDB data should be governed by strict service hooks so that only permitted operations and fields are accessible. Define hooks that validate context and enforce ownership or role checks before allowing create, update, or remove actions.
// src/services/items/hooks.js
const { iff, isProvider, preventChanges } = require('feathers-hooks-common');
exports.before = {
all: [],
find: [iff(isProvider('external'), preventChanges())], // prevent unwanted fields on public queries
get: [],
create: [ensureOwnsResourceOrAdmin],
update: [ensureOwnsResourceOrAdmin],
patch: [ensureOwnsResourceOrAdmin],
remove: [ensureOwnsResourceOrAdmin]
};
function ensureOwnsResourceOrAdmin(hook) {
return Promise.resolve().then(() => {
const { user } = hook.context;
if (!user) {
throw new Error('Unauthorized');
}
// Example: Dynamodb condition to ensure user owns the item
if (hook.data && hook.data.userId && hook.data.userId !== user.sub) {
throw new Error('Forbidden: cannot operate on another user\'s resource');
}
// For GET by id, validate ownership via a DynamoDB fetch in hook
return hook;
});
}
When using the official AWS SDK within FeathersJS hooks or services, ensure that requests use least-privilege IAM roles and that sensitive fields are masked in responses. Avoid returning raw DynamoDB attribute values to clients; project only necessary fields.
// src/services/items/index.js (Feathers service with DynamoDB adapter)
const { DynamoDBDocumentClient, ScanCommand } = require('@aws-sdk/lib-dynamodb');
const { ddbDocClient } = require('../../ddb'); // configured DynamoDB client
class ItemsService {
async find(params) {
const { user } = params.context || {};
const command = new ScanCommand({
TableName: process.env.DYNAMODB_TABLE,
FilterExpression: user ? 'userId = :uid' : undefined,
ExpressionAttributeValues: user ? { ':uid': user.sub } : undefined
});
const { Items } = await ddbDocClient.send(command);
// Return safe shape
return Items.map(({ userId, email, ...safe }) => safe);
}
}
module.exports = function () {
const app = this;
app.use('/items', new ItemsService());
};
3. Validate and sanitize inputs to avoid injection via UI interactions
Even when protected against embedding, ensure that inputs to FeathersJS services are validated so that clickjacking cannot be combined with injection to escalate impact. Use hooks to sanitize and validate data before DynamoDB operations.
// src/services/todos/hooks.js
const { validator } = require('feathers-hooks-common');
const titleValidator = validator({
title: { in: ['body'], isLength: { options: { min: 1, max: 100 } } }
});
exports.before = {
create: [titleValidator],
update: [titleValidator]
};
Combine these measures with regular security scans that include clickjacking tests to confirm that headers and CSP are effective in your deployment.