Null Pointer Dereference in Feathersjs with Jwt Tokens
Null Pointer Dereference in Feathersjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
A null pointer dereference in a FeathersJS application using JWT tokens occurs when the server attempts to access properties on an object that is null or undefined during request handling. This often happens after JWT validation or payload parsing, where expected data is missing or malformed. In FeathersJS, services and hooks rely heavily on context objects (context.params, context.result, and context.data). If a JWT token is accepted but does not carry required claims, or if the token payload is manipulated to omit critical fields, the application may proceed to access context.params.account.userId or similar paths without verifying existence.
For example, a hook that expects a decoded JWT payload with a user ID may crash if the token is forged or issued by an external system with a different structure. Consider a FeathersJS hook that retrieves the current user from the decoded token:
app.hooks(() => ({
before: {
async all(context) {
const { user } = context.params;
if (!user || !user.id) {
throw new Error('Invalid token payload');
}
context.params.requesterId = user.id;
}
}
}));
If context.params.user is undefined because the JWT was not properly attached or decoded, accessing user.id results in a null pointer dereference. This typically surfaces as an unhandled exception, leading to a 500 response that may leak stack traces or internal paths. In a black-box scan, such responses can indicate unstable error handling and potential information disclosure.
Another scenario involves service methods that assume the presence of data from a JWT-authenticated request. For instance, a users service method might directly use context.params.account.id without confirming it exists. If the authentication layer sets context.params.account to null under certain conditions (e.g., misconfigured hooks or missing middleware), any subsequent property access will trigger a null pointer dereference. This is particularly risky when the application integrates multiple authentication strategies or custom JWT verification logic that may not consistently populate the context.
During unauthenticated attack surface testing, an API that uses JWT tokens may still expose endpoints where context assumptions are not validated. A missing or malformed token might bypass authentication checks but leave context fields unset. Subsequent service logic then operates on incomplete data, increasing the likelihood of null pointer dereference. Such vulnerabilities can be chained with other findings like Improper Authentication or Excessive Data Exposure, especially if error messages reveal sensitive details about the application state.
Jwt Tokens-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on defensive checks before accessing nested properties derived from JWT payloads. Always validate the existence of expected claims and structure the context object before using them in services or hooks. Below are concrete, syntactically correct examples for FeathersJS that prevent null pointer dereference when working with JWT tokens.
Example 1: Hook-level validation with explicit null checks
const { AuthenticationError } = require('@feathersjs/errors');
app.hooks({
before: {
async all(context) {
const user = context.params && context.params.user;
if (!user || typeof user.id === 'undefined') {
throw new AuthenticationError('Invalid or missing JWT claims');
}
context.params.requesterId = user.id;
}
}
});
This pattern ensures that context.params and context.params.user are defined before accessing user.id. Throwing a structured error avoids unhandled exceptions and reduces information leakage.
Example 2: Service method defensive access
class UserService {
async find(params) {
const account = params && params.account;
if (!account || typeof account.id === 'undefined') {
throw new NotFound('Account information is missing');
}
// Safe to use account.id
return this.users.get(account.id);
}
}
module.exports = function () {
const app = this;
app.use('/users', new UserService());
};
In this service implementation, params and params.account are checked for existence and structure before any property access. This prevents null pointer dereference even when JWT-derived context is incomplete or manipulated.
Example 3: Centralized JWT payload normalization
function normalizeJwtPayload(context) {
const payload = context.params && context.params.user ? context.params.user : null;
if (!payload) {
return { isValid: false };
}
return {
isValid: true,
id: payload.id || null,
role: payload.role || 'guest'
};
}
app.hooks({
before: {
all(context) {
const normalized = normalizeJwtPayload(context);
if (!normalized.isValid) {
throw new Unauthenticated('JWT payload normalization failed');
}
context.params.normalizedUser = normalized;
}
}
});
This approach abstracts validation and normalization, making it easier to maintain consistent checks across hooks and services. It also reduces duplication and ensures that null pointer dereference is avoided by design.
Example 4: Using Feathers hooks common patterns safely
const { iff, isProvider } = require('feathers-hooks-common');
app.configure(hooks({
attachCurrentUser: iff(
isProvider('external'),
(context) => {
const user = context.params.user;
if (!user) {
throw new Unauthenticated('No user in context');
}
context.params.userId = user.sub || user.id;
return context;
}
)
}));
This uses well-established hook utilities while adding explicit guards. It demonstrates how to integrate defensive programming with FeathersJS ecosystem patterns to handle JWT tokens robustly.