Oct 7, 2026·6 min read·3 visits
A validation bypass in Payload CMS's polymorphic join filters allows authenticated users to query and infer the values of hidden or restricted fields.
Payload CMS is subject to an information disclosure vulnerability where users with query permissions can bypass field-level access controls. By leveraging polymorphic join filters, an attacker can perform blind-inference queries to retrieve restricted or hidden fields such as password reset tokens.
Payload CMS is an open-source headless content management system built on Node.js and Express. It supports rich querying capabilities, including polymorphic relations that link a single field to multiple potential target collections. These dynamic relationships require a specialized validation pipeline to verify incoming search parameters and query filters.\n\nThe vulnerability is located in the database query validation layer, specifically within the search parameter verification system. When a query is initiated, the system must validate that the requested fields exist and are accessible under the current user's security permissions. In polymorphic configurations, this process becomes complex because a queried path may only be valid for a subset of collections in the relationship union.\n\nAn administrative configuration or query permission on the base collection allows a low-privilege user to query dynamic fields. If a user constructs a payload referencing a hidden or restricted field within a polymorphic join, the validation checks fail to recognize that the target field is restricted rather than simply non-existent. This exposure allows unauthorized read access to highly sensitive properties across related collections.
The root cause lies in the validateSearchParam function within packages/payload/src/database/queryValidation/validateSearchParams.ts. During query validation, the pipeline attempts to resolve paths for fields specified in filter parameters. If a field is not found or is restricted, it is marked as invalid within the internal resolution context.\n\nTo accommodate polymorphic relationships, the system implemented a validation bypass. Because a field path in a polymorphic query may be absent from some of the target collections, the system is designed to ignore validation errors on missing fields to prevent false-positive validation rejections. This bypass was conditioned on whether the query was part of a polymorphic join, and whether the path conformed to a safe regex matching regular field structures.\n\nThe conditional check if (!polymorphicJoin || !SAFE_FIELD_PATH_REGEX.test(incomingPath)) failed to distinguish between a field that does not exist at all on the target collection and a field that exists but is restricted due to access control or being flagged as hidden: true. Consequently, when a query targeted a restricted field, the validation logic flagged it as invalid but then bypassed throwing an error, silently accepting the restricted parameter for database query execution.
An inspection of the vulnerable codebase reveals how the bypass suppressed critical security alerts. The search parameter validation loop iterated over paths, evaluating their validity. The original implementation was structured as follows:\n\ntypescript\n// Vulnerable Implementation\nif (invalid) {\n if (!polymorphicJoin || !SAFE_FIELD_PATH_REGEX.test(incomingPath)) {\n errors.push({ path })\n }\n}\n\n\nIn this logic, if invalid is true, the error is only pushed to the errors collection if the query is not a polymorphic join or if the path is unsafe. If polymorphicJoin is true and the path matches SAFE_FIELD_PATH_REGEX, the error condition is skipped. This design assumes that any validation failure on a polymorphic join is due to an unknown field, which is a critical design flaw.\n\nTo address this, the patch introduces a refinement that ensures the bypass is only applied if the field is genuinely unknown on the target collection. The corrected implementation utilizes the existence of the field definition to differentiate between absent and restricted fields:\n\ntypescript\n// Patched Implementation\nif (invalid) {\n const isUnknownPolymorphicJoinField =\n polymorphicJoin && !field && SAFE_FIELD_PATH_REGEX.test(incomingPath)\n\n if (!isUnknownPolymorphicJoinField) {\n errors.push({ path })\n }\n}\n\n\nUnder the patched logic, if the field exists within the schema definition (field is truthy) but is marked invalid due to access control or hidden status, isUnknownPolymorphicJoinField evaluates to false. As a result, the condition blocks the request and appends the validation error to the payload response, preventing database evaluation.
Exploitation of this vulnerability is achieved through a blind boolean or error-based inference attack. An attacker must possess at least minimal query permissions on a collection that maintains a polymorphic relationship to another collection containing sensitive data. The attacker does not need direct access to the sensitive collection itself.\n\nTo perform the attack, the adversary structures a query targeting the host collection, using the joins parameter to apply a filter on the polymorphic target. For example, if the Categories collection links polymorphically to Posts, and Posts contains a hidden field named hiddenSecret, the attacker can transmit a request with the query structure joins[polymorphicJoin][where][hiddenSecret][equals]=guess_value.\n\nBecause the validation layer fails to block the restricted field within the polymorphic context, the database evaluates the filter condition. By observing whether the host object is returned or if the join succeeds, the attacker can verify if the guessed value matches the actual database value. This technique can be automated to iteratively extract complex fields, including active password reset tokens or secret keys.
The impact of CVE-2026-105847 is high, as it permits the extraction of sensitive secrets, application tokens, and user credentials. The vulnerability represents a bypass of field-level access controls, which are a primary security boundary in headless CMS architectures. By exploiting this flaw, users with restricted read rights can escalate their privileges or compromise other accounts.\n\nThe Common Vulnerability Scoring System (CVSS) v4.0 assigns a base score of 7.1, reflecting high confidentiality impact on the vulnerable system. The attack vector is Network (AV:N), the attack complexity is Low (AC:L), and privileges required are Low (PR:L). No user interaction (UI:N) is necessary to execute the attack.\n\n> [!NOTE]\n> Although this vulnerability requires some privileges, in many headless CMS deployments, read permissions are granted to public or low-privilege API consumers. This configuration increases the risk of exploitation by external actors or compromised client applications.
Remediation requires upgrading the Payload CMS dependency to a version containing the official security patch. For production deployments running the 3.x branch, upgrade the payload package to version 3.90.0 or later. For deployments utilizing 4.x pre-release builds, update to 4.0.0-canary.34 or later.\n\nIf upgrading is not immediately possible, temporary workarounds can mitigate the risk. Security teams should audit application schemas to identify any polymorphic relationships. If dynamic joins are not strictly required by the client-side application, they can be restricted or disabled by implementing a global beforeOperation hook that intercepts and sanitizes the joins query parameters.\n\nAdditionally, Web Application Firewalls (WAF) can be configured to block request URIs or request bodies that contain dynamic join filters targeting known sensitive fields. Custom patterns should monitor for parameters matching joins[.*][where] combined with sensitive keys such as token, password, or secret to obstruct exploitation attempts.
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
payload payloadcms | >= 3.0.0, < 3.90.0 | 3.90.0 |
payload payloadcms | >= 4.0.0-canary.0, < 4.0.0-canary.34 | 4.0.0-canary.34 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-200, CWE-639 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 7.1 (High) |
| EPSS Score | N/A |
| Impact | Information Disclosure / Privilege Escalation |
| Exploit Status | Proof-of-Concept / Test-Suite Level |
| KEV Status | Not Listed |
The product exposes sensitive information to an actor who is not authorized to have access to that information.
CVE-2026-105805 is an authorization bypass and information disclosure vulnerability in Payload CMS. Before version 3.88.0, user-controlled sorting was executed at the database level before field-level access control rules and data redaction were applied. This allowed unauthorized users to reconstruct restricted field values through a sorting side-channel.
An open redirect vulnerability exists in Payload CMS within its Next.js-based authentication routing components. The sanitization utility fails to properly account for control characters and ambiguous encodings, allowing unauthenticated attackers to redirect users to external malicious domains after successful authentication.
A critical SQL Injection and access control bypass vulnerability was identified in Payload CMS database adapters (SQLite and PostgreSQL using Drizzle ORM internally). The vulnerability arises from case-sensitive logical operator checks during path validation and unvalidated sort queries. This allows remote attackers to bypass access control rules, execute unauthorized queries, and retrieve sensitive data through blind SQL injection side channels.
A critical prototype pollution vulnerability in the import-export plugin of Payload CMS allows unauthenticated remote attackers to bypass access controls and achieve remote code execution.
CVE-2026-105806 is an improper access control vulnerability within the Model Context Protocol (MCP) plugin for Payload CMS. Authenticated users with low privileges can manipulate API key creation and mapping to associate keys with arbitrary users, including administrators. This allows total session takeovers and privilege escalation via MCP-authenticated API requests.
An access control vulnerability in `@payloadcms/plugin-stripe` allows authenticated low-privilege users to bypass authorization boundaries and execute arbitrary, highly privileged operations on the connected Stripe platform via an exposed REST proxy.