Beast Attack in Strapi with Bearer Tokens
Beast Attack in Strapi with Bearer Tokens — how this combination creates or exposes the vulnerability
A Beast Attack (BOLA/IDOR) in Strapi with Bearer Tokens occurs when an access control check is incomplete or misaligned between the API surface and the authorization model. Strapi can be configured to issue JWTs or accept HTTP Bearer tokens for protected endpoints, but if object-level authorization is not enforced for each resource request, an authenticated attacker can manipulate identifiers to access other users' data.
Consider a Strapi backend exposing an endpoint like /api/users/:id that requires a Bearer token in the Authorization header. If the server validates the token and extracts a user identifier (e.g., sub or user id) but then directly uses the client-supplied :id parameter to fetch the record without verifying ownership, the BOLA (Broken Level Access) condition is present. For example, an attacker with a valid Bearer token can change :id from 101 to 102 and retrieve another user's profile because Strapi does not enforce that the record belongs to the token holder.
Bearer token usage can inadvertently encourage this pattern when developers assume token validation alone is sufficient. Strapi’s admin and plugins might expose REST or GraphQL routes where object ownership is inferred from the token but not explicitly checked against the requested resource. Attack tools such as Burp Suite can automate parameter mutation to enumerate IDs, and if Strapi responses differ for missing vs. unauthorized access, information leakage occurs. Compounded with verbose error messages, this can reveal whether a given ID exists and whether the token’s subject can access it, enabling account enumeration and lateral movement.
In practice, a Beast Attack against Strapi with Bearer Tokens involves: (1) intercepting a valid token; (2) crafting requests to Strapi endpoints such as GET /api/articles/:articleId; (3) modifying articleId to values owned by other users; and (4) observing whether Strapi returns the record, a 404, or a 403. If Strapi returns data for IDs that belong to other users, the API is vulnerable. This violates the principle of secure default authorization and can lead to mass data exposure, especially when tokens are long-lived or scoped broadly.
Strapi’s flexible content-type builder can increase risk if developers expose relations without considering cross-object authorization. For instance, an endpoint returning a user’s permissions might include references to other tenants or organizations; if those references are navigable without ownership checks, attackers can traverse relationships via manipulated IDs. Even with Bearer token protection, missing authorization checks on related resources create exploitable paths.
To detect this class of issue, scans compare the OpenAPI/Swagger spec definitions (including securitySchemes using bearerAuth) with runtime behavior. They verify whether authorization is applied consistently across operations and whether parameters are properly constrained by the subject derived from the token. Without runtime validation that ties each resource request to the token’s subject, Strapi APIs that rely solely on Bearer tokens remain susceptible to Beast Attacks.
Bearer Tokens-Specific Remediation in Strapi — concrete code fixes
Remediation centers on enforcing ownership checks for every resource request, even when a Bearer token is present. Do not rely on token possession alone; tie every data access to the token’s subject and ensure the subject matches the resource owner.
Strapi recommends using policies or middleware to perform explicit authorization. For REST endpoints, create a policy that extracts the subject from the token and compares it with the resource owner before returning data. Below is an example policy implementation that you can place in src/policies/ownership.js:
module.exports = async (ctx, next) => {
const { id } = ctx.params;
const authHeader = ctx.request.header.authorization;
let subjectId = null;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
ctx.throw(401, 'Unauthorized: Bearer token required');
}
const token = authHeader.split(' ')[1];
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
subjectId = payload.sub; // or payload.userId depending on your JWT format
} catch (err) {
ctx.throw(401, 'Invalid token');
}
const resource = await strapi.entityService.findOne('api::article.article', id, {});
if (!resource) {
ctx.throw(404, 'Not found');
}
if (String(resource.user.id) !== String(subjectId)) {
ctx.throw(403, 'Forbidden: You do not own this resource');
}
await next();
};
For GraphQL and dynamic routes, use resolver-level checks. In a Strapi GraphQL resolver, validate ownership before resolving the field:
const { id } = parent;
const { user } = ctx.state.user; // subject derived from Bearer token
if (String(user.id) !== String(id)) {
throw new Error('Forbidden');
}
When exposing content via the REST API, ensure the controller uses the policy above and does not skip authorization for ‘internal’ calls. Also, avoid returning 404 for owned resources and 403 for unowned ones in a way that leaks existence; consider uniform 403 responses where appropriate to reduce information leakage.
Rotate and protect your JWT secret (process.env.JWT_SECRET), set short token lifetimes, and scope tokens to least privilege. In Strapi Admin, review roles and permissions to ensure no overly broad scopes allow arbitrary resource access. For production, combine these code-level fixes with environment hardening and regular scans to verify that Bearer token protected endpoints enforce ownership consistently.