Data Exposure in Strapi with Api Keys
Data Exposure in Strapi with Api Keys
Strapi is a headless CMS that often exposes administrative and data endpoints through its REST and GraphQL interfaces. When API keys are used for authentication, misconfigurations can lead to Data Exposure, allowing unauthorized access to sensitive content such as user profiles, media uploads, and private fields. middleBrick identifies this as a distinct check under Data Exposure, validating whether valid API keys can retrieve records that should be restricted.
In Strapi, API keys are typically managed via roles and permissions. A common exposure scenario occurs when an API key assigned a limited role is used to query endpoints that return more data than intended. For example, a content API key might be allowed to read published articles but should never return draft entries, author email addresses, or internal relation IDs. If the content type permissions or field-level permissions are not strictly defined, the API key can over-permissively expose data. Strapi’s default behavior may include all fields in a response unless explicitly hidden, and developers sometimes rely on frontend filtering rather than server-side restrictions.
Another exposure pattern involves predictable identifiers and lack of ownership checks. Strapi’s REST endpoints often follow the pattern /api/articles/:id. If an API key can increment :id values and retrieve articles belonging to other users or contexts, this becomes a BOLA/IDOR issue that results in Data Exposure. Even with an API key, the system must enforce record-level authorization so that each key can only access data scoped to its tenant or user. GraphQL queries add complexity because a single request can fetch nested relations; without proper resolver restrictions, a key might pull sensitive user metadata embedded in related types.
middleBrick tests these scenarios without authentication by first checking whether any endpoints are unauthenticated, then validating whether provided API keys access data beyond their intended scope. The tool examines response payloads for PII, internal identifiers, and sensitive fields that should be masked or omitted. For instance, if a response includes fields such as resetPasswordToken, email, or role.permissions when using a content API key, this indicates a Data Exposure finding. Responses should be scoped to the minimum necessary data, and sensitive fields must be excluded based on the key’s role.
Api Keys-Specific Remediation in Strapi
Remediation focuses on tightening role permissions, using field and component policies, and ensuring responses exclude sensitive data for each API key. In Strapi, roles are defined under Settings → Roles & Permissions. For each API key role, explicitly allow only the necessary content types and actions, and configure which fields are returned.
First, create a dedicated role for your API consumer and disable unneeded actions such as findOne or delete if they are not required. Under Permissions for that role, set policies for each content type to Find only, and ensure Find One is disabled unless needed. Then, use the Fields settings to hide sensitive fields from the role. For example, hide email, resetPasswordToken, and internal flags from the content API key.
// Example: Define which fields are returned for the "contentApiKey" role
// This is configured visually in Strapi Admin, but conceptually maps to policies
// that control response shape. Ensure sensitive fields are unchecked for the role.
Second, leverage Strapi’s "Response Permissions" to fine-tune which attributes are included. In the Content Type Builder, open a content type, go to the specific field, and set its permissions per role. Disable fields like description or internalNotes for the API key role so they are omitted from responses even if the query requests them.
Third, enforce ownership and scope checks in custom controllers or before policies. If your data model includes a user or tenant identifier, add a before policy to ensure the requesting API key can only access records that match its scope. For example, if articles have a user_id field, a custom policy can compare the key’s associated user ID with the article’s user ID and reject mismatches.
// Example before policy in Strapi (pseudo-code for a custom policy)
module.exports = async (ctx, next) => {
const apiKey = ctx.state.user; // resolved from key validation
const articleId = ctx.params.id;
const article = await strapi.entityService.findOne('api::article.article', articleId);
if (!article) {
ctx.throw(404, 'Not found');
}
if (article.userId !== apiKey.userId) {
ctx.throw(403, 'Forbidden: Data scope mismatch');
}
await next();
};
Finally, validate that responses do not leak keys or tokens. Ensure API key payloads exclude authentication artifacts and that tokens are not stored in fields returned to the key. Use Strapi’s middleware to strip sensitive headers and configure CORS tightly. With these steps, Data Exposure findings related to API keys are mitigated, and the API returns only the data the key is permitted to see.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |