HIGH broken access controlsailsbearer tokens

Broken Access Control in Sails with Bearer Tokens

Broken Access Control in Sails with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Broken Access Control in Sails when Bearer Tokens are used arises from a mismatch between token-based authentication and Sails’ policy-based authorization. Sails does not enforce authentication or authorization by default; you add these via policies and integrations. When Bearer Tokens are accepted but not rigorously validated and bound to authorization checks, attackers can manipulate access by altering token claims, omitting tokens, or reusing tokens across users.

Common root causes include missing route-level policy enforcement, inconsistent token verification across controllers, and over-permissive policies such as allowing any authenticated user to update or delete records without verifying ownership or roles. For example, a policy that only checks req.user exists may pass for any valid Bearer Token, while the application logic fails to confirm that the user represented by the token owns the resource being accessed. This is a classic BOLA/IDOR pattern enabled by weak authorization tied to bearer authentication.

In Sails, if you use an authentication hook that sets req.user from a Bearer Token but do not enforce scope or role checks in every controller action, the unauthenticated attack surface includes endpoints that should be restricted to specific roles or resource owners. Attackers can probe endpoints with valid but low-privilege tokens, or with tokens lacking expected claims (e.g., missing scopes or roles), to identify authorization gaps. The 12 security checks in middleBrick run in parallel and include BOLA/IDOR and Authentication, which specifically test these weaknesses by validating that access controls are enforced per endpoint and that tokens cannot be trivially manipulated or reused across users.

Specification analysis further exposes risk: if your OpenAPI/Swagger spec defines security schemes using Bearer tokens but does not consistently reference security requirements on each operation, runtime findings may reveal endpoints that bypass authorization entirely. middleBrick cross-references spec definitions with runtime behavior to highlight mismatches, such as operations that require a token in the spec but are left open in implementation. This combination of bearer-based authentication and weak or inconsistent authorization policies increases the likelihood of unauthorized data access or modification, a key concern in OWASP API Top 10 Broken Object Level Authorization.

Bearer Tokens-Specific Remediation in Sails — concrete code fixes

Remediation focuses on tightly coupling Bearer Token validation with explicit authorization checks in Sails. Always verify token validity, scope, and claims before allowing access, and enforce ownership or role checks in your controllers or policies. Below are concrete code examples for secure Bearer Token handling in Sails.

1. Centralized token verification policy

Create an API policy that verifies the Bearer Token and attaches a normalized user object to req.user. This policy should reject requests without a valid token and ensure claims such as scope or roles are present.

// api/policies/bearer-auth.js
module.exports = async function (req, res, proceed) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.unauthorized('Missing Bearer Token');
  }
  const token = authHeader.split(' ')[1];
  try {
    const payload = await verifyToken(token); // use your JWT library, e.g., jsonwebtoken
    // Ensure required claims for authorization
    if (!payload.scope || !payload.scope.includes('api:read')) {
      return res.forbidden('Insufficient scope');
    }
    req.user = { id: payload.sub, role: payload.role, scope: payload.scope };
    return proceed();
  } catch (err) {
    return res.unauthorized('Invalid token');
  }
};

async function verifyToken(token) {
  // Example using jsonwebtoken (replace with your key/jwks)
  const jwt = require('jsonwebtoken');
  return jwt.verify(token, process.env.JWT_PUBLIC_KEY || 'your-secret');
}

2. Enforce ownership in controller or route-specific policy

After authentication, add an authorization check that ensures the requesting user can only access or modify their own resources. Never rely on authentication alone.

// api/policies/ensure-ownership-or-admin.js
module.exports = async function (req, res, proceed) {
  if (!req.user) {
    return res.unauthorized();
  }
  // For actions on a specific resource, e.g., /users/:id
  const targetId = req.param('id');
  if (req.user.role !== 'admin' && req.user.id !== targetId) {
    return res.forbidden('Access denied: insufficient permissions');
  }
  return proceed();
};

3. Example secured controller using both policies

Apply the Bearer token policy globally or per route, and layer ownership/admin checks on sensitive actions.

// api/controllers/UserController.js
module.exports = {
  find: [policy('bearer-auth'), async (req, res) => {
    const users = await User.find({ /* may filter by req.user if non-admin */ });
    return res.ok(users);
  }],

  update: [policy('bearer-auth'), policy('ensure-ownership-or-admin'), async (req, res) => {
    const user = await User.findOne(req.param('id'));
    if (!user) {
      return res.notFound();
    }
    const updated = await User.update(req.param('id')).set(req.body);
    return res.ok(updated);
  }],

  adminOnly: [policy('bearer-auth'), policy('require-role-admin'), async (req, res) => {
    const reports = await Report.findAll();
    return res.ok(reports);
  }],
};

// Policy to require a specific role
// api/policies/require-role-admin.js
module.exports = async function (req, res, proceed) {
  if (req.user && req.user.role === 'admin') {
    return proceed();
  }
  return res.forbidden('Admin role required');
};

4. OpenAPI/Swagger alignment

Ensure your spec declares the Bearer security scheme and consistently references it on operations that require authentication and appropriate scopes. This helps tools like middleBrick validate that runtime behavior matches the declared security requirements.

# openapi.yaml
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
security:
  - bearerAuth: [api:read, api:write]
paths:
  /users/{id}:
    put:
      security:
        - bearerAuth: [api:write]
      x-sails-policy: api/policies/ensure-ownership-or-admin

By combining verified Bearer Token handling with explicit, route-level authorization policies, you reduce the risk of Broken Access Control in Sails. middleBrick’s checks for Authentication, BOLA/IDOR, and related categories can help surface gaps in policy coverage and token validation.

Frequently Asked Questions

How does middleBrick detect missing authorization checks when Bearer Tokens are used in Sails?
middleBrick runs parallel checks including Authentication and BOLA/IDOR. It validates that endpoints requiring tokens reject requests without valid tokens, and that valid tokens cannot access resources they should not (e.g., by modifying ID parameters). Cross-referencing your OpenAPI/Swagger spec against runtime behavior highlights operations that declare security but lack proper enforcement.
Can middleware or global policies fully prevent Broken Access Control in Sails with Bearer Tokens?
Middleware or global policies reduce risk only if applied consistently to every route and combined with per-action authorization checks. A global Bearer token verification policy is necessary but not sufficient; you must also enforce ownership or role checks in controllers or route-specific policies to prevent IDOR and privilege escalation.