Disclosure Summary
Thanks to several community members and our internal security team, we have notified and patched five security vulnerabilities:
- CVE-2025-64526: GHSA-7mqx-wwh4-f9fw — Rate limit bypass on auth routes
- CVE-2026-22599: GHSA-3xcq-8mjw-h6mx — SQL injection in Content-Type Builder
- CVE-2026-22706: GHSA-hvp3-26wx-g2w4 — Password reset does not revoke existing refresh sessions
- CVE-2026-22707: GHSA-pcw7-5633-82vv — Upload plugin MIME validation bypass via Content API
- CVE-2026-27886: GHSA-rjg2-95x7-8qmx — Sensitive data leak via relational filtering
Per our security policy, we are performing our due diligence by publicly disclosing these vulnerabilities after careful testing, validation, and coordination with the reporting researchers. For further details of each patched vulnerability, please refer to the information below or consult the linked GitHub Advisories.
We would like to thank the following community members for their participation in our security program:
- adriatikii — CVE-2025-64526
- whiteov3rflow — CVE-2026-22599
- zaddy6, arthurgervais, jhoward1994, AndyAnh174, Aastha2602 — CVE-2026-22706 (independent concurrent submissions consolidated under a single advisory)
- kaminuma, arkmarta — CVE-2026-22707 (independent concurrent submissions consolidated under a single advisory)
- James Doll of WildWest CyberSecurity — CVE-2026-27886
Immediate resolution steps for all vulnerabilities disclosed here
To immediately resolve all vulnerabilities detailed in this post, please update your Strapi v5 packages to the latest available release (containing all five fixes). The minimum applicable versions are:
- v5.37.0 or later for CVE-2026-22599, CVE-2026-22706, CVE-2026-22707, and CVE-2026-27886
- v5.45.0 or later for CVE-2025-64526
- v4.26.1 or later for CVE-2026-22599 (v4 LTS only)
CVE-2026-22599: SQL Injection in Strapi Content-Type Builder
Summary of CVE-2026-22599 Vulnerability Details
- CVE: CVE-2026-22599
- CVSS v4.0 Vector:
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N(9.3 — Critical) - Affected Versions:
@strapi/content-type-builder<=5.33.1 (v5),@strapi/plugin-content-type-builder<=4.26.0 (v4) - How to Patch: Immediately update your Strapi to >=5.33.2 (v5) or >=4.26.1 (v4)
Description of CVE-2026-22599
A database query injection vulnerability existed in the Strapi Content-Type Builder write API. An authenticated administrator could inject arbitrary database statements through the column.defaultTo attribute when creating or modifying a content type. Setting defaultTo as a tuple [value, { isRaw: true }] caused the value to be passed directly into Knex's db.connection.raw() during schema migration without sanitization, allowing arbitrary statement execution at the database layer. Depending on the database engine, this enabled arbitrary file read via database utility functions, denial of service via forced server crash on schema-migration error, and, on engines that permit external program execution, remote code execution against the database server.
The patch addresses this by restricting all Content-Type Builder write APIs to development mode only. Production deployments running v5.33.2 or later return 404 for requests against /content-type-builder/content-types and related endpoints, removing the network-reachable attack surface entirely.
IoC's for CVE-2026-22599
Indicators that an instance running an unpatched version may have been exploited:
- HTTP access logs containing POST or PUT requests to
/content-type-builder/content-typesfrom a non-internal source. Regex pattern:(POST|PUT)\s+/content-type-builder/ - Database server logs containing unexpected DEFAULT clause values that reference filesystem-access or program-execution helper functions of your database engine
- Strapi server crashes immediately following a content-type creation or update, observed as the Node process exiting during the schema-migration step
- Files appearing under unexpected paths on the database host that match content-type DEFAULT values from the application
- Newly-created content-types named or shaped to extract specific data (attribute names like
passwd,etc,env,config)
CVE-2026-22706: Password Reset Does Not Revoke Existing Refresh Sessions
Summary of CVE-2026-22706 Vulnerability Details
- CVE: CVE-2026-22706
- CVSS v4.0 Vector:
CVSS:4.0/AV:N/AC:H/AT:N/PR:H/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N(2.1 — Low) - Affected Versions:
@strapi/adminand@strapi/plugin-users-permissions<=5.33.2 - How to Patch: Immediately update your Strapi to >=5.33.3
Description of CVE-2026-22706
In Strapi versions prior to 5.33.3, changing or resetting a user's password did not invalidate the user's existing refresh-token sessions by default. The refresh-token invalidation step in the users-permissions and admin authentication controllers was conditional on a caller-supplied deviceId. When a password change or reset request did not include a deviceId, no refresh tokens were revoked, leaving every prior session active.
An attacker who had previously obtained a refresh token could continue minting new access tokens after the legitimate user reset their password, allowing persistent unauthorized access for the lifetime of the refresh token (up to 30 days by default). Rotating credentials no longer terminates an active attacker session, defeating password reset as a containment measure.
The patch invalidates all refresh tokens associated with the user on every password change and password reset, regardless of whether a deviceId is supplied. A new device-scoped session is then issued to the caller as part of the response.
IoC's for CVE-2026-22706
Indicators that an instance running an unpatched version may have been exploited:
- Successful
POST /api/auth/refreshorPOST /admin/access-tokenrequests using a refresh token issued before the user's most recent password change. Reviewable by correlating refresh-tokeniatclaims against password-change events in audit logs - New access-token issuances for a user whose password was reset within the past 30 days, originating from an IP or User-Agent that did not perform the reset
- Multiple active refresh tokens for a single user across distinct IPs after a password reset event
- Database query: rows in
strapi_sessionwithcreated_atearlier than the user's most recent password-reset timestamp andstatus = 'active'
CVE-2026-22707: Upload Plugin MIME Validation Bypass via Content API
Summary of CVE-2026-22707 Vulnerability Details
- CVE: CVE-2026-22707
- CVSS v4.0 Vector:
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N(5.3 — Medium) - Affected Versions:
@strapi/upload<=5.33.2 - How to Patch: Immediately update your Strapi to >=5.33.3
Description of CVE-2026-22707
In Strapi versions prior to 5.33.3, the Upload plugin's Content API endpoints did not enforce the administrator-configured MIME type restrictions (plugin.upload.security.allowedTypes and deniedTypes). The same restrictions were correctly enforced on the Admin Panel upload path.
The upload plugin's enforceUploadSecurity security check was invoked in the admin upload controller, but was missing from the Content API controller. The Content API handlers uploadFiles and replaceFile (and the upload wrapper that dispatches to them) called the underlying upload service directly, bypassing both the magic-byte MIME detection and the configured allow/deny lists.
An authenticated user with the Content API upload permission could therefore upload file types the administrator had explicitly disallowed, including HTML and SVG content. In deployments serving uploaded files from the same origin as the admin panel (default), an attacker could upload an HTML or SVG file that, when opened directly by an admin, executed JavaScript in the admin origin, enabling admin-session hijack and authenticated administrative actions against the admin API.
The patch introduces a shared prepareUploadRequest helper that wraps enforceUploadSecurity and is called from both the Content API and admin upload controllers, ensuring identical security policy enforcement on every upload entry point.
IoC's for CVE-2026-22707
Indicators that an instance running an unpatched version may have been exploited:
- Files in
/uploads/with extensions outside the configured allow-list, particularly.html,.htm,.svg,.js,.mjs,.xml, or.xhtml. Filesystem regex:\.(html?|svg|m?js|x?html|xml)$ - Successful 201 responses from
POST /api/uploadwhere the uploaded file's MIME or extension is outside the configuredallowedTypes - Server access logs showing non-administrator users uploading files with executable web content types. Content-Type regex:
text/html|application/javascript|image/svg\+xml - Admin browsing logs (X-Forwarded-For, User-Agent) opening files under
/uploads/*.htmlor/uploads/*.svgshortly before unexpected administrative actions (user creation, role changes, permission modifications)
CVE-2026-27886: Sensitive Data Leak via Relational Filtering Without Query Sanitization
Summary of CVE-2026-27886 Vulnerability Details
- CVE: CVE-2026-27886
- CVSS v4.0 Vector:
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N(9.2 — Critical) - Affected Versions:
@strapi/strapi<=5.36.1 - How to Patch: Immediately update your Strapi to >=5.37.0
Description of CVE-2026-27886
Strapi versions prior to 5.37.0 did not sufficiently sanitize query parameters when filtering content via relational fields. An unauthenticated attacker could use the where query parameter on any publicly-accessible content-type with an updatedBy (or other admin-relation) field to perform a boolean-oracle attack against private fields on the joined admin_users table, including the resetPasswordToken field. Extracting an admin reset token via this oracle made full administrative account takeover possible without authentication.
When a filter such as where[updatedBy][resetPasswordToken][$startsWith]=a was applied to a public Content API endpoint, the underlying query generation performed a LEFT JOIN against the admin_users table and emitted a WHERE clause referencing the joined column. The query parameter sanitization layer did not block operator chains that traversed into relational target schemas the caller had no read permission on, allowing the response count to be used as a one-bit oracle on any admin-table field.
The patch introduces explicit query-parameter sanitization at the controller and service boundary via three new primitives: strictParam, addQueryParams, and addBodyParams. Operator chains that traverse into restricted relational targets are now rejected before reaching the database.
IoC's for CVE-2026-27886
Indicators that an instance running an unpatched version may have been exploited:
- Server access logs containing query strings traversing into admin-relation private fields. Regex:
\?(.*&)?where\[(updatedBy|createdBy|publishedBy)\]\[(email|password|resetPasswordToken|confirmationToken|firstname|lastname|preferedLanguage)\]\[\$(startsWith|contains|eq|gt|lt|ge|le|in|notIn|notNull|null)\]= - High volume of public Content API requests from a single IP iterating through a hex alphabet (
0-9,a-f) on the same content-type endpoint with progressively longer filter values - Subsequent
POST /admin/reset-passwordcalls using a reset token that the legitimate admin did not request - Successful admin password change immediately following a burst of public Content API requests with
where[updatedBy]query parameters - Sustained burst of identical-shape requests with only the trailing character of the filter value varying
CVE-2025-64526: Rate Limit Bypass on Auth Routes via Body Email Manipulation
Summary of CVE-2025-64526 Vulnerability Details
- CVE: CVE-2025-64526
- CVSS v4.0 Vector:
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N(6.9 — Medium) - Affected Versions:
@strapi/plugin-users-permissions<=5.44.0 - How to Patch: Immediately update your Strapi to >=5.45.0
Description of CVE-2025-64526
In Strapi versions prior to 5.45.0, the rate-limit middleware in the users-permissions plugin derived its rate-limit key in part from ctx.request.body.email, including on routes whose body schema does not contain an email field (/auth/local, /auth/reset-password, /auth/change-password). An unauthenticated attacker could include an arbitrary email value in the request body to obtain a fresh rate-limit key per request, effectively bypassing per-IP throttling on those routes and enabling high-volume credential brute-force, password-reset code brute-force, and credential-stuffing attempts.
The rate-limit key was constructed as ${userIdentifier}:${requestPath}:${ctx.request.ip}, where userIdentifier = ctx.request.body.email. On routes that legitimately use email as their identifier (e.g., /auth/forgot-password, /auth/local/register), this scoping is correct. On routes that use a different identifier (identifier for login, code for password reset, currentPassword for password change), the email field was not part of the route contract, but the middleware still incorporated it into the key, allowing a caller to rotate the value and obtain a unique key on every request.
The patch maintains an allow-list of routes that legitimately key on the email field and excludes that key component from every other route on which the middleware is mounted. OAuth callback paths (/connect/*) are treated identifier-less. On routes outside the allow-list, the middleware now falls back to a fixed identifier-less key, ensuring per-IP throttling remains effective even when the request body is attacker-controlled.
IoC's for CVE-2025-64526
Indicators that an instance running an unpatched version may have been exploited:
- Unusually high volumes of
POSTrequests to/api/auth/local,/api/auth/reset-password, or/api/auth/change-passwordfrom a single IP within a 5-minute window without 429 (Too Many Requests) responses - Request bodies on
/api/auth/localcontaining bothidentifierANDemailfields whereemailvaries per request. Body shape regex:"identifier"\s*:\s*"[^"]*",\s*"email"\s*:\s*"[^"]*" - Request bodies on
/api/auth/reset-passwordcontaining an unexpectedemailfield alongsidecode. Body shape regex:"code"\s*:\s*"[^"]*",.*"email"\s*: - Server logs showing many distinct rate-limit key prefixes for the same IP+route combination within the rate-limit window
- Successful authentication or password reset following hundreds of preceding 401/400 responses from the same IP
A Note on Report Volume and Triage Timelines
We want to be transparent with our community: over the past several months, we have seen a significant increase in the volume of vulnerability reports submitted to our security program, driven largely by the proliferation of AI-assisted vulnerability scanning and report generation. While we welcome legitimate research and continue to value every submission, the reality is that roughly 92% of the reports we now receive are not valid — they describe non-issues, dependency-only concerns outside our codebase, configuration choices framed as flaws, or AI-generated reports that do not reproduce against Strapi.
Each report still has to be read, validated, and responded to by a human on our security team, and the increased volume has stretched our triage timelines. We are actively working to streamline our process, but contributors and customers should be aware that initial response and validation may take longer than they have historically. Valid, well-documented reports with clear reproduction steps continue to receive priority, and we remain deeply grateful to the researchers who invest the time to submit quality work.
Commitment to Responsible Disclosure
We at Strapi believe in responsible disclosure. For each of the vulnerabilities in this post, we worked with the reporting security researchers to ensure that the vulnerabilities were patched before public disclosure. Once each vulnerability was patched, we added a notice to our release notes to inform users that a security issue had been resolved, and we notified our customers and partners directly via email so they could upgrade ahead of detailed public disclosure.
We are grateful to every researcher who participated in this disclosure for their professionalism and patience. Their work helps us improve the security of Strapi for the entire community.
We urge anyone who believes they have discovered a security vulnerability to assist us in responsibly disclosing it by submitting a GitHub Advisory on our main repo or by contacting our security team via security@strapi.io.
Thanks,
The Strapi Security Team