VYPR
Low severityNVD Advisory· Published Mar 10, 2026· Updated Mar 10, 2026

Craft has a potential information disclosure vulnerability in preview tokens

CVE-2026-29113

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.

PackageAffected versionsPatched versions
craftcms/cmsPackagist
>= 4.0.0-RC1, < 4.17.44.17.4
craftcms/cmsPackagist
>= 5.0.0-RC1, < 5.9.75.9.7

Affected products

2

Patches

1
6a88468dc35a

Fixed GHSA-vg3j-hpm9-8v5v

https://github.com/craftcms/cmsbrandonkellyFeb 7, 2026via ghsa
6 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 modified
  • src/web/assets/cp/dist/cp.js.map+1 1 modified
  • src/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

News mentions

0

No linked articles in our index yet.