HIGH privilege escalationfeathersjsjwt tokens

Privilege Escalation in Feathersjs with Jwt Tokens

Privilege Escalation in Feathersjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Privilege escalation in a FeathersJS application that uses JWT tokens typically occurs when authorization checks are incomplete or when tokens carry excessive permissions. FeathersJS is a framework-agnostic REST and real-time API layer; it does not enforce authorization by default. If you add JWT support and only verify the token’s signature and expiry, an attacker can modify the decoded payload (e.g., changing role claims) and gain higher privileges unless the server re-validates authorization on each request.

In Feathers, authentication and authorization are often implemented via hooks. A common misconfiguration is to trust client-supplied role fields after verifying the JWT, instead of deriving authorization from a server-side identity store. For example, if the JWT payload includes a role like admin, and the client sends that token with each request, the server might skip additional checks and assume the role is trustworthy. This becomes a BOLA/IDOR and privilege escalation vector when endpoints do not enforce per-resource ownership or role-based access control (RBAC).

An attack flow might look like this: a user authenticates and receives a JWT with role: "user". The attacker modifies the local token (if not using HTTPS and secure storage) or tricks the app into using a tampered token. Because Feathers services do not automatically validate claims beyond authentication, an altered token with role: "admin" can pass through hooks and reach services that should be restricted. If the service performs minimal checks (e.g., only checking params.user.role derived from the token), the attacker can execute administrative actions, leading to unauthorized data access or operations.

Another scenario involves endpoints that accept user-supplied IDs without verifying that the requesting user has the right to act on that resource. Even with JWT authentication, if the authorization logic does not cross-check the token’s subject (e.g., sub or userId) with the resource being accessed, an attacker can change the ID in the request (BOLA) and elevate their ability to view or modify other users’ data. MiddleBrick’s checks for BOLA/IDOR and Privilege Escalation are designed to surface these gaps by comparing authenticated findings with the API specification and runtime behavior.

To detect such issues, scanning tools analyze the OpenAPI spec for missing security schemes on sensitive operations and verify that each service enforces explicit authorization. They also inspect hook logic to ensure that authorization is derived from a trusted server-side source rather than from the JWT payload alone. Without these safeguards, JWT-based authentication in FeathersJS can inadvertently expose privilege escalation paths.

Jwt Tokens-Specific Remediation in Feathersjs — concrete code fixes

Remediation centers on ensuring that JWT tokens are verified server-side and that authorization is enforced independently of claims embedded in the token. Always validate the token signature, expiry, and issuer on the server, and avoid trusting any payload fields for authorization decisions without additional checks.

Use Feathers hooks to centralize authorization. For example, define a hook that loads the user from a server-side data store using the authenticated user’s ID (from the verified JWT) and attaches it to params.user. Then, in your service logic or another hook, verify that the user is allowed to perform the action on the target resource.

// server/hooks/authentication.js
const { AuthenticationError } = require('@feathersjs/errors');
const jwt = require('jsonwebtoken');

module.exports = function (options) {
  return async context => {
    const { authorization } = context.headers;
    if (!authorization || !authorization.startsWith('Bearer ')) {
      throw new AuthenticationError('Not authenticated');
    }
    const token = authorization.slice(7);
    let payload;
    try {
      // Use your server-side secret or public key
      payload = jwt.verify(token, process.env.JWT_SECRET);
    } catch (error) {
      throw new AuthenticationError('Invalid token');
    }
    // Attach minimal trusted data to context; do not rely on client-supplied roles
    context.params.user = {
      id: payload.sub,
      // Do not copy roles from the token; derive roles from your database
    };
    return context;
  };
};

After authentication, enforce RBAC at the service or application level. Do not rely on a role claim inside the JWT. Instead, fetch the user’s roles from your database and check them before proceeding.

// server/hooks/authorize-role.js
module.exports = function (requiredRole) {
  return async context => {
    const user = context.params.user;
    if (!user || !user.id) {
      throw new context.app.errors.Forbidden('Unauthorized');
    }
    // Fetch user from your data store to get current roles
    const userRecord = await context.app.service('users').get(user.id);
    if (!userRecord.roles || !userRecord.roles.includes(requiredRole)) {
      throw new context.app.errors.Forbidden('Insufficient permissions');
    }
    // Optionally attach enriched user info back to context
    context.params.user = userRecord;
    return context;
  };
};

Apply these hooks to your Feathers services. In your service definition, configure hooks to run on relevant methods:

// server/services/items/items.hooks.js
const authenticate = require('./authentication');
const authorizeAdmin = require('./authorize-role');

module.exports = {
  before: {
    all: [authenticate()],
    find: [],
    get: [],
    create: [authorizeAdmin('admin')],
    update: [authorizeAdmin('admin')],
    patch: [authorizeAdmin('admin')],
    remove: [authorizeAdmin('admin')]
  },
  after: {},
  error: {}
};

Additionally, configure your JWT secret and token settings securely. Avoid embedding sensitive logic in the client. When using the JWT strategy with Feathers’ authentication module, ensure the secret is stored in environment variables and that tokens have a reasonable expiry to limit the impact of token leakage.

// server/src/app.js
const authentication = require('@feathersjs/authentication');
const jwt = require('@feathersjs/authentication-jwt');

app.configure(authentication({
  secret: process.env.AUTH_SECRET
}));
app.configure(jwt({
  secret: process.env.JWT_SECRET,
  // Set proper token expiry; avoid long-lived tokens for sensitive operations
  expiresIn: '1h'
}));

By combining verified JWT authentication with server-side role resolution and explicit hook-based authorization, you mitigate privilege escalation risks. This approach ensures that even if a token is compromised or manipulated, the server does not accept untrusted claims for authorization.

Frequently Asked Questions

Why should I avoid copying role claims from JWTs into authorization logic in FeathersJS?
Because JWT payloads can be tampered with if not protected by strict validation and secure transmission. Authorization should be derived from a trusted server-side identity store, not from client-controlled token claims, to prevent privilege escalation.
How does MiddleBrick detect insufficient authorization in FeathersJS APIs using JWTs?
MiddleBrick scans your OpenAPI spec and runtime behavior to check whether sensitive endpoints rely solely on token claims for authorization, whether ownership and role checks are enforced per request, and whether hooks properly validate and re-derive permissions from a server-side identity.