Clickjacking in Feathersjs with Api Keys
Clickjacking in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side attack that tricks a user into interacting with a hidden or disguised UI element, often by loading the target application in an invisible iframe. In a FeathersJS application that uses API keys for authentication, clickjacking can expose key-sensitive operations even when the API key is not transmitted via cookies. An attacker may embed a FeathersJS service endpoint inside an iframe and overlay interactive elements (e.g., buttons or forms) to coerce a logged-in user into making unintended requests that include the API key in headers.
Consider a FeathersJS service that accepts an API key via an Authorization header. If the application does not enforce X-Frame-Options or Content-Security-Policy (CSP) frame-ancestors, an attacker can host a page that loads https://api.example.com/messages inside a transparent iframe. Even though the API key is not stored in localStorage and is sent only in headers, the browser will include credentials such as cookies or HTTP authentication if the FeathersJS service allows cross-origin requests without proper CORS configuration. Combined with missing anti-CSRF tokens for state-changing methods (e.g., POST, DELETE), this can lead to unauthorized actions being performed under the authenticated user’s context.
The risk is elevated when API keys are used for service-to-service or elevated permissions. For example, a FeathersJS hook that relies solely on an API key header for authorization may not validate the origin of the request, enabling an attacker to trigger dangerous operations (like changing settings or accessing sensitive data) via a forged UI. The API key itself is not leaked directly through clickjacking, but the combination of a predictable endpoint, missing frame restrictions, and over-permissive CORS can allow an attacker to leverage the user’s authenticated state to interact with the API in unintended ways.
middleBrick’s 12 security checks include testing for missing anti-clickjacking protections and improper CORS configuration, which can highlight these risks during scans of FeathersJS endpoints. By analyzing both the OpenAPI specification and runtime behavior, the scanner can detect whether frame-ancestors directives and key-bearing requests are sufficiently isolated from embedded contexts.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
To mitigate clickjacking risks in a FeathersJS application that uses API keys, apply defense-in-depth measures that combine HTTP headers, strict CORS policies, and secure service hooks. Below are concrete, working examples tailored to FeathersJS services.
1. Set anti-clickjacking and CSP headers
Ensure your FeathersJS server sets X-Frame-Options and CSP frame-ancestors to prevent embedding. If you are using Express-compatible middleware (common with Feathers), add these headers before the Feathers service is initialized.
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const app = express(feathers());
// Security headers to prevent clickjacking
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader(
'Content-Security-Policy',
"frame-ancestors 'none';"
);
next();
});
// Continue with REST and Socket.io setup
app.configure(express.rest());
app.configure(express.socketio());
app.use('/messages', require('./services/messages'));
2. Tighten CORS to restrict origins
Configure CORS to allow only trusted origins. This prevents unauthorized sites from making credentialed requests that could be leveraged in clickjacking scenarios.
const cors = require('@feathersjs/transportation-cors');
app.configure(cors({
origin: 'https://your-trusted-frontend.com',
credentials: true
}));
3. Validate origins in hooks for API-key-sensitive operations
In FeathersJS hooks, explicitly check the request origin for sensitive methods, especially when API keys are used in headers. This adds a runtime safeguard beyond headers.
// In your service hooks
const allowedOrigin = 'https://your-trusted-frontend.com';
app.service('messages').hooks({
before: {
create: [context => {
const origin = context.params.headers.origin;
if (origin !== allowedOrigin) {
throw new Error('Invalid origin');
}
// Optionally verify context.params.headers['x-api-key'] if used
return context;
}]
}
});
4. Use anti-CSRF tokens for state-changing methods
Even with API keys, ensure that state-changing operations validate a CSRF token when the request originates from a browser context. This prevents forged forms or iframes from successfully interacting with your Feathers services.
// Example of checking a CSRF token in a create hook
app.service('transactions').hooks({
before: {
create: [context => {
const { 'x-csrf-token': csrfToken } = context.params.headers;
const expected = context.params.user.csrfToken; // stored server-side per session
if (!csrfToken || csrfToken !== expected) {
throw new Error('Invalid CSRF token');
}
return context;
}]
}
});
5. Example of secure API key usage in a Feathers service
Below is an example Feathers service that expects an API key in the headers and performs basic validation, while being protected by the above headers and hooks.
// services/secure-data/secure-data.class.js
const { Service } = require('@feathersjs/feathers');
class SecureDataService {
find(params) {
const apiKey = params.headers['x-api-key'];
if (!apiKey || apiKey !== process.env.SERVICE_API_KEY) {
throw new Error('Unauthorized');
}
return [{ id: 'secure-item', value: 'restricted' }];
}
}
module.exports = function () {
const app = this;
app.use('/secure-data', new SecureDataService());
};