Broken Access Control in TinaCMS Authorization
A broken access control vulnerability in @tinacms/auth allows attackers to perform unauthorized actions by supplying their own valid TinaCloud credentials against a victim's TinaCMS deployment.
The @tinacms/auth package contains a critical broken access control vulnerability (confirmed at commit 5a6839f) that permits unauthorized cross-tenant access. The isAuthorized(req) function performs authorization by validating a bearer token against an identity provider endpoint (https://identity.tinajs.io/v2/apps/${req.query.clientID}/currentUser). Crucially, the function retrieves the clientID from the user-provided request parameters rather than comparing it against the site's locally configured TinaCloud application ID.
An attacker with a standard TinaCloud account can exploit this by creating their own application, obtaining a valid token, and submitting requests to a victim site with their own clientID and token. The victim's application incorrectly validates the credentials against the attacker's own app, returning an authorized response. This vulnerability exposes critical functionality, including media bucket management and full GraphQL content read/write/delete capabilities when using the default TinaCloudBackendAuthProvider. The flaw exists across multiple integration libraries, including next-tinacms-cloudinary, next-tinacms-azure, and next-tinacms-dos.
Attack Chain
- Attacker registers a free TinaCloud account and creates a personal application to obtain a valid
clientIDand bearer token. - Attacker identifies a target self-hosted TinaCMS site that utilizes the vulnerable
@tinacms/authpackage or its derived media-store integrations. - Attacker crafts a malicious HTTP request (e.g.,
GET /api/cloudinary/media) targeting the victim's API endpoint. - Attacker includes their own
clientIDas a query parameter and their own valid TinaCloud bearer token in theAuthorizationheader. - The victim's backend calls
isAuthorized(req), which incorrectly performs a look-up atidentity.tinajs.iousing the attacker-suppliedclientID. - The identity provider returns a successful validation status because the credentials are valid for the attacker's own app.
- The victim's backend logic, failing to pin the
clientIDto the site-specific ID, returns anauthorized: trueresponse to the media or GraphQL handler. - The attacker performs unauthorized actions, such as reading private media, uploading arbitrary files to the victim's CDN, or deleting/modifying site content.
Impact
Successful exploitation allows an unauthenticated attacker to gain editor-level control over unrelated TinaCMS tenants. Impacts include the ability to list, read, or delete sensitive files within the victim's media bucket. Furthermore, when TinaCloudBackendAuthProvider is active, the attacker gains full GraphQL access, enabling the exfiltration of site content or the injection of malicious data into the CMS.
Recommendation
- Immediately audit all instances of
@tinacms/authand associated media/backend providers in self-hosted TinaCMS deployments. - Implement strict server-side validation to ensure the
clientIDprovided in incoming requests matches the hard-coded or environment-configured application ID for that specific site. - Avoid relying solely on the return value of
isAuthorized(req)without verifying that the returneduserobject's associatedappIdmatches the expected deployment identifier. - Block or monitor suspicious inbound requests to
/api/cloudinary/mediaor/api/tina/gqlwhere theclientIDparameter does not match your organization's known TinaCloudclientID.
Immediate actions
Review all self-hosted TinaCMS deployments for hard-coded clientID validation.
Threat Hunt
Search logs for requests to /api/tina/gql or /api/cloudinary/media where clientID parameter is present.
Data: Webserver logs
Enrichment needed
- Affected deployment inventory. (CTI) Identify which internal sites are vulnerable.
Mitigations
Pin clientID in the authorized callback to the site-specific value.
Broken Access Control in TinaCMS
Gaps
- Need visibility into GraphQL query parameters in web logs.
Indicators of compromise
1
domain
| Type | Value |
|---|---|
| domain | identity.tinajs.io |