Craft has a potential information disclosure vulnerability in preview tokens
Description
Craft is a content management system (CMS). Prior to 4.17.4 and 5.9.7, Craft CMS has a CSRF issue in the preview token endpoint at /actions/preview/create-token. The endpoint accepts an attacker-supplied previewToken. Because the action does not require POST and does not enforce a CSRF token, an attacker can force a logged-in victim editor to mint a preview token chosen by the attacker. That token can then be used by the attacker (without authentication) to access previewed/unpublished content tied to the victim’s authorized preview scope. This vulnerability is fixed in 4.17.4 and 5.9.7.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A CSRF vulnerability in Craft CMS's preview token endpoint allows an attacker to force a logged-in editor to mint a chosen token, enabling unauthenticated access to unpublished content.
Vulnerability
Craft CMS versions prior to 4.17.4 and 5.9.7 contain a Cross-Site Request Forgery (CSRF) vulnerability in the preview token creation endpoint at /actions/preview/create-token. The endpoint accepts a user-supplied previewToken parameter without requiring a POST request or validating a CSRF token, making it possible for an attacker to force a logged-in victim editor to create a preview token of the attacker's choosing [1][3].
Exploitation
To exploit this vulnerability, an attacker must know the target content's canonicalId and public URL path. The attacker prepares a fixed 32-character token value and crafts a malicious link that directs the victim to the endpoint with the attacker's token and target parameters. If the victim is logged into Craft's control panel and has an active preview authorization for the target content (e.g., they have recently edited an entry), the endpoint will create the token specified by the attacker [3]. This attack can be delivered via a simple link or top-level redirect, requiring no additional authentication on the attacker's part.
Impact
A successful attack allows the attacker to replay the known token without authentication to access the victim's authorized preview scope, which includes draft, provisional, and revision content that would otherwise be unpublished. This leads to information disclosure of sensitive or in-progress content. The vulnerability has been rated as low-severity by the vendor [2][3].
Mitigation
The vulnerability is fixed in Craft CMS versions 4.17.4 and 5.9.7. The fix ensures that when a previewToken is supplied, the system validates it as signed data using Security::validateData() and rejects invalid tokens with a BadRequestHttpException. Additionally, the token generation now uses a signed (hashed) preview token to prevent tampering [2][3]. Users should update to the patched versions immediately.
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
craftcms/cmsPackagist | >= 4.0.0-RC1, < 4.17.4 | 4.17.4 |
craftcms/cmsPackagist | >= 5.0.0-RC1, < 5.9.7 | 5.9.7 |
Affected products
2Patches
16 files changed · +17 −5
CHANGELOG.md+1 −0 modified@@ -4,6 +4,7 @@ - Fixed a bug where Dashboard columns weren’t getting refreshed when the window was resized. ([#18389](https://github.com/craftcms/cms/issues/18389)) - Fixed a [low-severity](https://github.com/craftcms/cms/security/policy#severity--remediation) XSS vulnerability. (GHSA-fvwq-45qv-xvhv) +- Fixed a [low-severity](https://github.com/craftcms/cms/security/policy#severity--remediation) information disclosure vulnerability. (GHSA-vg3j-hpm9-8v5v) ## 4.17.2 - 2026-01-28
src/controllers/ElementsController.php+4 −1 modified@@ -384,6 +384,8 @@ public function actionEdit(?ElementInterface $element, ?int $elementId = null): } $security = Craft::$app->getSecurity(); + $previewToken = $previewTargets ? $security->generateRandomString() : null; + $notice = null; if ($element->isProvisionalDraft) { $notice = fn() => $this->_draftNotice(); @@ -446,7 +448,8 @@ public function actionEdit(?ElementInterface $element, ?int $elementId = null): 'isProvisionalDraft' => $element->isProvisionalDraft, 'isUnpublishedDraft' => $isUnpublishedDraft, 'previewTargets' => $previewTargets, - 'previewToken' => $previewTargets ? $security->generateRandomString() : null, + 'previewToken' => $previewToken, + 'hashedPreviewToken' => $previewToken ? $security->hashData($previewToken) : null, 'previewParamValue' => $previewTargets ? $security->hashData(StringHelper::randomString(10)) : null, 'revisionId' => $element->revisionId, 'siteId' => $element->siteId,
src/controllers/PreviewController.php+7 −0 modified@@ -63,6 +63,13 @@ public function actionCreateToken(): Response $token = $this->request->getParam('previewToken'); $redirect = $this->request->getParam('redirect'); + if ($token) { + $token = Craft::$app->getSecurity()->validateData($token); + if ($token === false) { + throw new BadRequestHttpException('Request contained an invalid preview token'); + } + } + if ($draftId) { $this->requireAuthorization('previewDraft:' . $draftId); } elseif ($revisionId) {
src/web/assets/cp/dist/cp.js+1 −1 modifiedsrc/web/assets/cp/dist/cp.js.map+1 −1 modifiedsrc/web/assets/cp/src/js/ElementEditor.js+3 −2 modified@@ -892,7 +892,7 @@ Craft.ElementEditor = Garnish.Base.extend( canonicalId: this.settings.canonicalId, siteId: this.settings.siteId, revisionId: this.settings.revisionId, - previewToken: this.settings.previewToken, + previewToken: this.settings.hashedPreviewToken, }; if (this.settings.draftId && !this.settings.isProvisionalDraft) { @@ -957,7 +957,7 @@ Craft.ElementEditor = Garnish.Base.extend( return previewUrl; } - if (!this.settings.previewToken) { + if (!this.settings.previewToken || !this.settings.hashedPreviewToken) { throw 'Missing preview token'; } @@ -2279,6 +2279,7 @@ Craft.ElementEditor = Garnish.Base.extend( isUnpublishedDraft: false, previewTargets: [], previewToken: null, + hashedPreviewToken: null, previewParamValue: null, revisionId: null, siteId: null,
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-vg3j-hpm9-8v5vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-29113ghsaADVISORY
- github.com/craftcms/cms/commit/6a88468dc35a27cccc8fef254f415a447d4a07ccghsax_refsource_MISCWEB
- github.com/craftcms/cms/security/advisories/GHSA-vg3j-hpm9-8v5vghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.