HIGH insecure designfeathersjsjavascript

Insecure Design in Feathersjs (Javascript)

Insecure Design in Feathersjs with Javascript — how this specific combination creates or exposes the vulnerability

FeathersJS is a JavaScript framework for building real-time APIs. When the framework’s default project scaffolding and service patterns are used without deliberate secure design, the resulting API surface often maps closely to the OWASP API Top 10 category Insecure Design. Insecure design is not a single bug but a set of architectural decisions that expose excessive attack surface, allow over-permissive data access, and weaken authorization boundaries.

With FeathersJS, a common insecure design is creating a “generic” service that maps 1:1 to a database table and relying on Feathers’ default CRUD hooks without enforcing property-level or row-level authorization. Because JavaScript is dynamically typed, it is easy to write service files that accept arbitrary payloads and pass them directly to the database layer without validating or sanitizing fields. This becomes especially risky when combined with features like feathers-authentication and the feathers-hooks-common utilities, which can inadvertently propagate user-controlled data into sensitive operations if the hooks are not explicitly constrained.

Another insecure design pattern in JavaScript-based Feathers apps is missing or weak ownership/tenant checks. A service might verify that a user is authenticated but fail to scope queries to the user’s own resources. In JavaScript, because object property access is loose, it is straightforward to accidentally build a query like find({ query: { userId: currentUser.id } }) but then allow clients to override or omit the userId via query parameters or by sending it in the request body. The dynamic nature of JavaScript makes it easy for such checks to be bypassed through parameter pollution or unexpected merging of defaults and user input.

Real-world attack patterns such as BOLA/IDOR and BFLA/Privilege Escalation often exploit these design gaps. For example, an attacker may send crafted PATCH/PUT requests to modify fields that should be immutable (like role flags or admin status), leveraging JavaScript’s flexible object manipulation to set properties that the server inadvertently accepts. Insecure deserialization patterns in JavaScript (e.g., passing raw JSON to service methods without a strict schema) can also enable injection or prototype pollution, which may be chained into SSRF or unauthorized data access. Because Feathers services often integrate with multiple transports (REST and Socket.io), an insecure design in one channel can propagate risk to another, and JavaScript’s runtime flexibility can make it harder to detect misuse early.

To detect such issues, middleBrick runs 12 security checks in parallel, including Authentication, BOLA/IDOR, BFLA/Privilege Escalation, Property Authorization, Input Validation, and Unsafe Consumption. These checks analyze both static definitions (including OpenAPI/Swagger specs with full $ref resolution) and runtime behavior, ensuring that insecure design patterns in JavaScript Feathers APIs are surfaced with severity ratings and prioritized remediation guidance.

Javascript-Specific Remediation in Feathersjs — concrete code fixes

Remediation focuses on explicit schema validation, strict query scoping, and disciplined hook configuration. In JavaScript, prefer static typing aids like ajv or schema validation libraries to enforce input shapes before data reaches service methods. Always scope data access to the current user and avoid relying on client-supplied identifiers for ownership checks.

Example of insecure Feathers service and hook setup:

// Insecure service file: services/messages/messages.service.js
const { Service } = require('feathers-mongoose');
class MessageService extends Service {
  async create(data, params) {
    // Insecure: directly using data.userId allows client to set any user
    return super.create(data, params);
  }
}
module.exports = function (app) {
  app.use('/messages', new MessageService({ Model: app.get('mongoose').models.Message }));
};

Fix with explicit ownership scoping and validation:

// services/messages/messages.service.js
const { Service } = require('feathers-mongoose');
const { iff, isProvider } = require('feathers-hooks-common');
const Ajv = require('ajv');
const ajv = new Ajv();

const messageSchema = {
  type: 'object',
  required: ['text'],
  properties: {
    text: { type: 'string', minLength: 1, maxLength: 500 },
    userId: { type: 'string', format: 'mongo' }
  },
  additionalProperties: false
};

const validateMessage = (data) => {
  const validate = ajv.compile(messageSchema);
  const valid = validate(data);
  if (!valid) throw new Error(validate.errors.map(e => e.message).join('; '));
  return data;
};

class SecureMessageService extends Service {
  async create(data, params) {
    // Validate shape and reject extra properties
    const validated = validateMessage(data);
    // Enforce ownership by ignoring any client-supplied userId
    validated.userId = params.user._id;
    return super.create(validated, params);
  }
}

module.exports = function (app) {
  app.use('/messages', new SecureMessageService({ Model: app.get('mongoose').models.Message }));

  const messageHooks = {
    before: {
      create: [
        iff(isProvider('external'), validateMessage),
        iff(isProvider('external'), (hook) => {
          // Remove client-supplied userId if present
          delete hook.data.userId;
          // Set owner to authenticated user from hook
          hook.data.userId = hook.params.user._id;
          return hook;
        })
      ],
      update: [iff(isProvider('external'), (hook) => {
        // Prevent privilege escalation by blocking role/admin changes via client
        if (hook.data.role !== undefined) throw new Error('Forbidden: role cannot be updated');
        return hook;
      })]
    }
  };
  app.service('/messages').hooks(messageHooks);
};

Additionally, ensure that your service queries are scoped consistently. Avoid patterns where an attacker can supply a userId that overrides the server-side scope:

// Potentially insecure query in a Feathers service method
const results = await app.service('documents').find({
  query: { userId: userIdFromRequest } // vulnerable if userIdFromRequest comes from client
});

// Secure alternative: enforce server-side ownership
const results = await app.service('documents').find({
  query: { userId: params.user._id, $limit: 50 }
});

For APIs using REST transports, combine these practices with OpenAPI spec constraints and runtime monitoring. middleBrick’s checks include OWASP API Top 10 mappings and compliance references (PCI-DSS, SOC2, HIPAA, GDPR), helping you prioritize fixes based on severity and exploit likelihood.

Frequently Asked Questions

How does insecure design differ from implementation bugs in FeathersJS APIs?
Insecure design refers to architectural decisions that expose excessive attack surface or weaken authorization boundaries, such as allowing clients to dictate ownership identifiers or accepting arbitrary payloads without schema validation. Implementation bugs are specific code defects; insecure design issues persist even if dependencies are patched and require changes to API contracts, hooks, and validation strategy.
Can middleBrick detect insecure design patterns in FeathersJS JavaScript APIs?
Yes. middleBrick runs parallel security checks including BOLA/IDOR, BFLA/Privilege Escalation, Property Authorization, and Input Validation. It analyzes both static definitions (including OpenAPI/Swagger specs with full $ref resolution) and runtime behavior to surface insecure design patterns with severity ratings and remediation guidance.