Broken Object Level Authorization

Broken Object Level Authorization in Node.js Express [CVE-2026-40291]

[Updated Apr 2026] Updated CVE-2026-40291

Overview

CVE-2026-40291 documents an insecure direct object modification vulnerability in Chamilo LMS where an authenticated user with ROLE_STUDENT could escalate to ROLE_ADMIN by altering the roles field on their own user record via PUT /api/users/{id}. The underlying issue was that the API Platform security check is_granted('EDIT', object) only verified ownership, and the writable serialization group included roles, enabling arbitrary role changes. This is a canonical example of Broken Object Level Authorization (BOLA) and maps to a similar class of issues in Node.js/Express where a handler trusts client-supplied object changes rather than enforcing strict authorization. The CVE highlights CWE-269 (Incorrect Access Control) and CWE-863 (Incorrect Authorization). In Node.js/Express, a parallel vulnerability occurs when an endpoint updates a user document directly from req.body without proper permission checks, allowing privilege escalation through writable fields like roles.

Code Fix Example

Node.js (Express) API Security Remediation
/* Vulnerable */
const express = require('express');
const app = express();
app.use(express.json());

// Imagine a Mongoose-like User model
// const User = require('./models/User');

app.put('/api/users/:id', async (req, res) => {
  // No explicit authorization check
  const user = await User.findById(req.params.id);
  if (!user) return res.status(404).send('Not found');
  // Vulnerable: directly applying client-provided updates, including roles
  Object.assign(user, req.body);
  await user.save();
  res.json(user);
});

/* Fixed */
const hasRole = (reqUser, role) => Array.isArray(reqUser?.roles) && reqUser.roles.includes(role);

app.put('/api/users/:id', async (req, res) => {
  // Assumes req.user is populated by authentication middleware
  const user = await User.findById(req.params.id);
  if (!user) return res.status(404).send('Not found');

  // Authorization: allow admins to update any user, or users to update non-sensitive fields only
  if (req.user?.id !== user.id && !hasRole(req.user, 'ROLE_ADMIN')) {
    return res.status(403).send('Forbidden');
  }

  // Whitelist safe fields; do not allow role changes by non-admins
  const { roles, password, ...safeUpdates } = req.body;
  Object.assign(user, safeUpdates);

  // If an admin is performing the update and explicitly provides roles, apply them
  if (hasRole(req.user, 'ROLE_ADMIN') && typeof roles !== 'undefined') {
    user.roles = roles;
  }

  await user.save();
  res.json(user);
});

CVE References

Choose which optional cookies to allow. You can change this any time.