Type Confusion in Feathersjs with Api Keys
Type Confusion in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
Type confusion in FeathersJS when handling API keys occurs when the service layer does not enforce strict types or schemas for key values, allowing an attacker to supply values that are interpreted as different JavaScript types. FeathersJS is typically configured with authentication handlers that validate credentials; if an API key is accepted as a loosely typed parameter (for example, from query strings, headers, or body fields) without normalization to a consistent string type, the application may inadvertently coerce values in comparisons or serialization.
Consider a FeathersJS service that uses a custom authentication strategy based on API keys. If the service retrieves the key from the request headers and compares it using loose equality (==) or relies on dynamic casting, an attacker may exploit type confusion by sending values such as numeric IDs, arrays, or objects that, when coerced, match the key unintentionally. For example, if the expected key is a string like sk_live_abc123, but the comparison logic does not enforce type checks, a numeric value like 123 might be coerced to the string "123", bypassing intended authorization checks when the service logic is flawed.
In OpenAPI specifications for FeathersJS-based APIs, if the API key parameter is defined with an ambiguous type (e.g., type: string without explicit pattern constraints or as type: object in error-prone configurations), runtime validation may not catch type mismatches. This becomes critical when the API key is used in authorization decisions across multiple services or when the spec is generated from loosely typed models. Cross-referencing spec definitions with runtime behavior using tools like middleBrick can reveal mismatches where expected string-based keys are handled in a way that permits type confusion, potentially exposing endpoints to IDOR-like behavior or privilege escalation when keys are misinterpreted as other entities.
Another angle involves serialization and deserialization in FeathersJS hooks. If API keys are passed through hooks without enforcing a consistent type—such as converting all incoming identifiers to strings before comparison—an attacker may supply structured input (e.g., JSON objects or arrays) that the hook misinterprets, leading to unauthorized access. Because FeathersJS allows flexible hook configuration, developers must ensure that API key handling includes explicit type checks and normalization to prevent confusion between string, number, or object inputs.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
To remediate type confusion around API keys in FeathersJS, enforce strict type checks and normalize all key inputs to strings before comparison or authorization. Avoid loose equality operators and prefer strict equality (===) with explicit type validation. Below are concrete code examples demonstrating secure handling of API keys in a FeathersJS service.
Example 1: Custom authentication handler with strict type checks
const { AuthenticationService, Params } = require('@feathersjs/authentication');
class ApiKeyAuthenticationService extends AuthenticationService {
async create(data, params) {
const { apiKey } = data;
// Ensure apiKey is a string and matches expected pattern
if (typeof apiKey !== 'string') {
throw new Error('Invalid API key type');
}
const normalizedKey = apiKey.trim();
const pattern = /^sk_live_[a-f0-9]{32}$/;
if (!pattern.test(normalizedKey)) {
throw new Error('Invalid API key format');
}
// Proceed with lookup and validation
return { authentication: { strategy: 'api-key', apiKey: normalizedKey } };
}
}
module.exports = function (app) {
const authentication = new AuthenticationService(app);
app.set('authentication', authentication);
};
Example 2: Hook to normalize and validate API keys before service logic
const { iff, isProvider } = require('feathers-hooks-common');
function normalizeApiKeyHook() {
return async context => {
if (context.data && context.data.apiKey) {
const key = context.data.apiKey;
if (typeof key !== 'string') {
throw new Error('API key must be a string');
}
context.data.apiKey = key.trim();
}
return context;
};
}
// Apply hook to a service
app.service('secure-endpoint').hooks({
before: {
create: [iff(isProvider('external'), normalizeApiKeyHook())]
}
});
Example 3: OpenAPI 3.0 spec with strict schema for API key
paths:
/api/resource:
post:
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- apiKey
properties:
apiKey:
type: string
pattern: '^sk_live_[a-f0-9]{32}$'
description: 'API key must be a string matching the live key pattern.'
responses:
'200':
description: OK
Example 4: Using strict equality in service methods
class SecureService {
async find(params) {
const { apiKey } = params.query;
const storedKey = process.env.LIVE_API_KEY;
// Strict type and value check
if (typeof apiKey !== 'string' || apiKey !== storedKey) {
throw new Error('Unauthorized');
}
return [];
}
}
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |