Brute Force Attack in Strapi with Api Keys
Brute Force Attack in Strapi with Api Keys — how this specific combination creates or exposes the vulnerability
A brute force attack against Strapi using API keys occurs when an attacker attempts to discover a valid key through rapid, automated requests. Strapi can be configured to use API keys as a bearer token for certain routes, especially when using the strapi-plugin-users-permissions or custom policies. If an API key is embedded in client-side JavaScript, leaked in logs, or exposed through an insecure endpoint, it becomes a target for credential stuffing or exhaustive search.
Unlike password-based brute forcing, API key brute force does not involve account lockouts by default if the key is presented as a static bearer token. The risk arises when the key is weak, predictable, or shared across environments, and when Strapi does not enforce additional rate limiting on the endpoint that validates the key or on authenticated routes that rely on it. Attackers may probe unauthenticated discovery endpoints, such as the /api/content-manager/explorer or custom GraphQL endpoints, to enumerate which routes require authorization and then attempt token guessing.
Because middleBrick scans the unauthenticated attack surface, it can detect whether public endpoints inadvertently expose API key acceptance paths and whether rate limiting is insufficient to deter high-volume requests. Even when API keys are used, if Strapi lacks IP-based throttling or global request capping, an attacker can iterate through key values and observe differences in HTTP status codes or response times to infer validity. This becomes more impactful when the API key grants elevated permissions, such as content publishing or administrative scopes, increasing the potential for data exposure or modification within the CMS.
The scan also checks whether API keys are transmitted over non-encrypted channels, which would expose them to interception. Encryption and transport security are essential complements to API key usage. Without proper input validation on key acceptance points, Strapi may inadvertently leak stack traces or internal paths that aid an attacker in refining their approach. middleBrick’s checks for Rate Limiting, Input Validation, and Authentication are particularly relevant in this context, as they surface gaps that make API key brute force feasible.
Furthermore, if Strapi is integrated with external services or uses API keys to call downstream APIs, the risk extends beyond the CMS itself. An attacker who compromises a key could pivot to linked systems, especially if those services lack their own strict authentication or if the keys are long-lived. middleBrick’s checks for Data Exposure and Unsafe Consumption help identify whether API keys are stored or transmitted insecurely, and whether responses contain sensitive data that should be protected.
Api Keys-Specific Remediation in Strapi — concrete code fixes
To mitigate brute force risks tied to API keys in Strapi, apply server-side controls that limit guessing opportunities and harden key handling. Begin by enforcing strict rate limiting on all endpoints that accept bearer tokens, including custom routes in ./src/api/*/controllers. Use Strapi’s built-in policies to wrap sensitive actions and apply a token bucket or sliding window algorithm via middleware.
Enabling rate limiting on Strapi routes
In Strapi v4, you can configure policies in ./src/api/api-name/config/policies.js. Create a custom policy that checks a request header against a store of recent attempts:
// src/api/api-name/config/policies.js
module.exports = {
rateLimitPerKey: {
handler: async (ctx, next) => {
const key = ctx.request.header['x-api-key'];
const cacheKey = `ratelimit:apikey:${key || ctx.state.user?.id || 'anonymous'}`;
const current = await ctx.state.redis?.get(cacheKey);
const limit = 60; // requests per window
if (current && parseInt(current, 10) >= limit) {
ctx.status = 429;
ctx.body = { error: 'Too Many Requests' };
return;
}
await ctx.state.redis?.incr(cacheKey);
await ctx.state.redis?.expire(cacheKey, 60); // 60-second window
await next();
},
config: {},
},
};
Then apply this policy to a route in ./src/api/api-name/config/routes.json:
{
"routes": [
{
"method": "GET",
"path": "/reports",
"handler": "report.find",
"config": {
"policies": ["rateLimitPerKey"]
}
}
]
}
Validating and restricting API key usage
Do not accept API keys in query strings; enforce their use only in the Authorization header as a Bearer token. Validate the key format before it reaches business logic to avoid timing attacks by using constant-time comparison where feasible. In your service file, require a valid key pattern:
// src/api/api-name/services/report.js
const VALID_PREFIX = 'mb_live_';
const VALID_LENGTH = 32;
function validateApiKey(key) {
if (typeof key !== 'string') return false;
if (!key.startsWith(VALID_PREFIX)) return false;
if (key.length !== VALID_LENGTH) return false;
// Optionally verify against a database hashed record
return true;
}
module.exports = {
find: async (params) => {
const provided = params?.query?.apiKey || params.state?.user?.apiKey;
if (!validateApiKey(provided)) {
throw new Error('Invalid API key');
}
// Proceed with safe data access
return strapi.entityService.findMany('api::report.report', {
filters: { /* ... */ },
});
},
};
Restricting key scope and rotation
Use scoped API keys that limit endpoints and operations. Store keys encrypted at rest and rotate them on a schedule. Avoid embedding keys in frontend code; instead, use short-lived tokens derived from a key when necessary. middleBrick’s findings for Authentication and Data Exposure help verify that keys are not served over HTTP and that permissions are appropriately scoped.