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.