Broken Authentication in Feathersjs with Cockroachdb
Broken Authentication in Feathersjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
Broken Authentication in a Feathersjs service backed by Cockroachdb typically arises from a mismatch between Feathersjs application logic and database-side controls. Feathersjs is a framework that favors minimal built-in authentication; it relies on explicit configuration of authentication handlers and hooks. When authentication is misconfigured or omitted, an attacker can call service methods directly without a valid subject or token. Because Cockroachdb is strongly consistent and supports granular role-based privileges, the database can enforce constraints, but those constraints must be correctly mapped to Feathersjs hooks and services.
One common pattern is a Feathersjs service that connects to Cockroachdb with a connection pool using a role that has broader privileges than intended. If the service does not validate ownership or scope per request, horizontal privilege escalation becomes feasible. For example, a /users/:id endpoint that does not enforce that the requesting user can only access their own row allows BOLA/IDOR regardless of database-level row policies. Attackers may also probe unauthenticated endpoints; Feathersjs allows service registration without authentication, and if those services interact with Cockroachdb without applying tenant or user filters, data exposure occurs.
Another vector is credential handling. Feathersjs can integrate with authentication libraries such as @feathersjs/authentication-local, but if passwords are stored weakly in Cockroachdb (e.g., plaintext or with a weak hash), authentication is broken regardless of transport security. Attackers may leverage common weaknesses like missing rate limiting on login services to perform credential stuffing. Because Cockroachdb provides ACID guarantees, brute-force or timing-based attacks can be more predictable if the server’s response times leak validation state.
Middleware and hooks are central in Feathersjs. If before-hooks do not normalize identifiers (e.g., resolving a user ID from a JWT and attaching it to the params), subsequent hooks or the service handler may inadvertently operate on the wrong subject. With Cockroachdb, this can lead to operations being applied to rows the user should not touch. Insecure consumption patterns, such as passing raw query parameters directly into Knex or Sequelize-style query builders without validation, enable injection or privilege escalation.
Finally, configuration details matter. If the Feathersjs application connects to Cockroachdb using a service account with high privileges and does not enforce row-level security at the application layer, a single compromised endpoint can lead to wide data exposure. Proper remediation requires coordinated changes in the Feathersjs service configuration, hook implementation, and Cockroachdb role grants and policies to ensure least privilege and subject-level enforcement.
Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on tightening authentication checks, enforcing ownership at the database level, and ensuring least-privilege database roles. Below are concrete, syntactically correct examples for a Feathersjs service interacting with Cockroachdb using Knex-style query building.
- Define a restricted Cockroachdb role for the application user and grant minimal privileges:
-- In Cockroachdb, create a role with SELECT/INSERT/UPDATE limited to necessary columns
grant select, insert, update (id, email, hashed_password, tenant_id) on users to app_reader_writer;
revoke delete on users from app_reader_writer;
-- Ensure row-level security via application-side checks; Cockroachdb does not enforce RLS natively in all deployments
- Feathersjs service configuration with authentication and hooks that enforce ownership:
const { AuthenticationService } = require('@feathersjs/authentication');
const { expressOauth } = require('@feathersjs/authentication-oauth');
const { iff, isProvider } = require('feathers-hooks-common');
app.use('/users', new AuthenticationService({
entity: 'user',
service: app.service('users'),
paginate: { default: 25, max: 50 }
}));
app.service('users').hooks({
before: {
all: [ auth.hooks.authenticate(['jwt']) ],
find: [ (context) => {
// Ensure queries are scoped to the authenticated subject
if (context.params.user) {
context.params.query.userId = context.params.user.id;
// Optionally restrict by tenant if applicable
if (context.params.user.tenantId) {
context.params.query.tenantId = context.params.user.tenantId;
}
}
return context;
}],
get: [ (context) => {
// Enforce that users can only retrieve their own profile
const userId = context.params.user.id;
context.params.query = { id: userId };
return context;
}],
create: [ (context) => {
// Set the subject on creation if not provided
if (context.params.user) {
context.data.userId = context.params.user.id;
}
return context;
}],
update: [ (context) => {
// Prevent updating other users’ records
const userId = context.params.user.id;
if (context.id !== undefined && context.id !== userId) {
throw new Error('Forbidden: cannot update other user records');
}
context.params.query = { id: userId };
return context;
}],
patch: [ (context) => {
// Apply ownership filter for partial updates
const userId = context.params.user.id;
context.params.query = { id: userId };
return context;
}]
},
after: [ iffy(isProvider('external'), sanitizePublic) ],
});
- Explicit input validation and query scoping in a custom hook to prevent unsafe consumption:
const sanitizeQuery = (context) => {
const allowedFields = ['email', 'profile'];
const query = context.params.query || {};
// Remove potentially dangerous keys
delete query.$where;
delete query.$customData;
// Whitelist fields
if (query.select) {
const selectFields = query.select.split(',').filter(f => allowedFields.includes(f.trim()));
query.select = selectFields.join(',');
}
// Ensure tenant or user scope is applied when available
if (context.params.user && !query.userId) {
query.userId = context.params.user.id;
}
context.params.query = query;
return context;
};
- Example of a login route that enforces rate limiting and secure password comparison using the Feathersjs authentication service with Cockroachdb-backed entities:
const local = require('@feathersjs/authentication-local').authentication;
app.use('/login', local({
service: app.service('users'),
usernameField: 'email',
passwordField: 'password'
}));
// Ensure the users service uses hashed passwords and a strong hash like bcrypt
app.service('users').hooks({
before: {
create: [ (context) => {
// Assume a hook hashes the password before storage
if (context.data.password) {
context.data.password = hashPassword(context.data.password);
}
return context;
}]
}
});
These examples emphasize that broken authentication is not only about transport security but also about proper scoping, subject validation, and least-privilege database access patterns. MiddleBrick scans can surface misconfigured authentication flows and unsafe consumption patterns; its findings map to frameworks such as OWASP API Top 10 and compliance regimes like SOC2 and GDPR. For continuous assurance, the Pro plan enables scheduled scans and alerts, while the CLI allows you to integrate checks into scripts and pipelines.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |