Broken Access Control in Restify with Mongodb
Broken Access Control in Restify with Mongodb — how this specific combination creates or exposes the vulnerability
Broken Access Control occurs when an API fails to enforce proper authorization checks between subjects and resources. In a Restify service that uses Mongodb as the backend, the risk arises when endpoints rely on implicit trust, missing role or ownership checks, or incorrectly resolve user identifiers before querying the database.
Consider a Restify endpoint designed to fetch a user profile:
server.get('/profiles/:id', async (req, res, next) => {
const { id } = req.params;
const profile = await db.collection('profiles').findOne({ _id: new ObjectId(id) });
res.send(profile);
});
If the handler does not verify that the authenticated user is allowed to view the requested id, an attacker can iterate through valid ObjectIds and access any profile. This is a classic Broken Access Control pattern (BOLA/IDOR) in the Restify + Mongodb stack. Attackers exploit missing ownership or scope checks to read, modify, or delete other users' data.
Another common pattern is using query parameters without validating the requester's role:
server.get('/admin/users', async (req, res, next) => {
const users = await db.collection('users').find({}).toArray();
res.send(users);
});
If this route lacks role-based authorization (e.g., ensuring the caller is an admin), any authenticated user who discovers the endpoint can enumerate sensitive user records. Restify does not enforce permissions automatically; developers must explicitly encode authorization logic.
With Mongodb, additional risk appears when field-level permissions are handled incorrectly. For example, a query that conditionally includes sensitive fields based on a user role may omit runtime checks:
const projection = isAdmin ? { email: 1, ssn: 1 } : { email: 1 };
const user = await db.collection('users').findOne({ _id: userId }, { projection });
If isAdmin is derived from an unchecked request value rather than a verified session or role claim, an attacker can manipulate the parameter to exfiltrate protected fields. This couples Broken Access Control with Data Exposure, a high-severity combination.
Insecure default configurations in Mongodb deployments (such as open network access or weak authentication) further amplify the impact. Even when Restify enforces some checks, a misconfigured database can allow direct access to collections, bypassing application logic entirely. This underscores the need to align application-layer authorization with infrastructure controls.
middleBrick scans such endpoints during a 5–15 second black-box pass, checking authentication, authorization boundaries, and data exposure. Its LLM/AI Security module can detect prompt-injection-style probes that attempt to coax unintended data disclosure via crafted inputs, complementing traditional API security checks.
Mongodb-Specific Remediation in Restify — concrete code fixes
To remediate Broken Access Control in Restify with Mongodb, enforce strict ownership checks and role-based access at the handler level, and avoid leaking database internals through error messages.
First, always resolve the subject from the authenticated session rather than trusting client-provided identifiers:
server.get('/profiles/me', async (req, res, next) => {
const userId = req.user.id; // authenticated subject from session/JWT
const profile = await db.collection('profiles').findOne({ _id: userId });
if (!profile) return next(new restify.NotFoundError());
res.send(profile);
});
For operations where an ID is provided, compare the requested ID to the authenticated subject:
server.get('/profiles/:id', async (req, res, next) => {
const userId = req.user.id;
const requestedId = req.params.id;
if (userId !== requestedId) {
return next(new restify.ForbiddenError('Access denied'));
}
const profile = await db.collection('profiles').findOne({ _id: new ObjectId(requestedId) });
res.send(profile);
});
Second, enforce role checks server-side before returning sensitive data:
server.get('/admin/users', async (req, res, next) => {
if (req.user.role !== 'admin') {
return next(new restify.ForbiddenError('Insufficient permissions'));
}
const users = await db.collection('users').find({}).toArray();
res.send(users);
});
Third, derive projection rules from verified server-side roles, not from unvalidated input:
function projectionForUser(user) {
if (user.role === 'admin') {
return { email: 1, ssn: 1, permissions: 1 };
}
return { email: 1 };
}
server.get('/users/me', async (req, res, next) => {
const userId = req.user.id;
const user = await db.collection('users').findOne({ _id: userId }, {
projection: projectionForUser(req.user)
});
res.send(user);
});
Fourth, use MongoDB’s role-based access controls where possible and avoid broad read permissions for sensitive collections. Configure connection strings with least-privilege credentials and enable TLS to protect data in transit.
Finally, structure error handling to avoid leaking database details:
server.get('/users/:id', async (req, res, next) => {
try {
const userId = req.user.id;
const requestedId = req.params.id;
if (userId !== requestedId) {
return next(new restify.ForbiddenError('Access denied'));
}
const user = await db.collection('users').findOne({ _id: new ObjectId(requestedId) });
if (!user) return next(new restify.NotFoundError());
res.send(user);
} catch (err) {
next(new restify.InternalServerError('Request failed'));
}
});
These steps align with OWASP API Top 10 controls for Broken Access Control and help ensure that authorization is enforced consistently. middleBrick’s Pro plan supports continuous monitoring, so changes to endpoints or roles can be flagged before deployment.