Cache Key Collision Leads to Node Access Bypass
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3593384. -->
Reported by: [smustgrave](https://www.drupal.org/user/3252890)
Related to !183
>>>
<h3 id="summary-problem-motivation">Problem/Motivation</h3>
<p><strong>This was originally reported as a private security issue but cleared by the security team for a public issue</strong></p>
<p>A low-privileged authenticated user may be able to view unpublished or otherwise access-restricted child nodes through the printer-friendly export functionality after cache priming by a higher-privileged user.<br>
The observed behavior appears related to subtree caching and access filtering during Book export rendering.</p>
<h4 id="summary-steps-reproduce">Steps to reproduce</h4>
<ol>
<li>Enable the Book module.</li>
<li>Create a public root node and add it as a new book.</li>
<li>Create a child node under the book hierarchy and mark it unpublished.</li>
<li>Create a low-privileged authenticated role with:</li>
</ol><ul>
<li>View published content</li>
<li>View printer-friendly books</li>
</ul>
<li>Verify the low-privileged user receives HTTP 403 when directly accessing the unpublished child node.</li>
<li>Clear caches:</li>
<li>As an administrator (or any user with view access to the unpublished child), visit: /book/export/html/{root_nid}</li>
<li>This populates the persistent cache (cache.data bin) under the key book-links:subtree-data:{root_nid} with a tree containing the unpublished child node.</li>
<li>Without clearing caches in between, switch to the low-privileged user session and visit: /book/export/html/{root_nid}</li>
<li>Sanity: with the low-privileged user, verify the following still return HTTP 403:</li>
<ul>
<li> /node/{child_nid}</li>
<li> /book/export/html/{child_nid}</li>
</ul>
<li>This confirms that the user genuinely lacks view access to the restricted child; the leak is solely through the parent export.</li>
<p>Observed result:<br>
The export response in step 8 includes content from the unpublished child node.<br>
Expected result:<br>
Restricted child nodes should not appear in export output for users lacking node access permissions.</p>
<p>Expected result:<br>
Restricted child nodes should not appear in export output for users lacking node access permissions.</p>
<p>Technical observations:</p>
<ul>
<li>BookManager::bookSubtreeData() caches subtree data using: book-links:subtree-data:{nid}</li>
<li>The cache key does not appear to include user-specific access context.</li>
<li>The chained cache backend defined in book.services.yml (book.backend_chained_cache) appends the persistent cache.data SQL bin, which is shared across all users and survives across requests.</li>
<li>The cache row can be observed directly: SELECT cid, expire FROM cache_data WHERE cid LIKE 'book-links:subtree-data:%';</li>
<li>The export route validates access for the root node parameter, while subtree rendering later relies on the cached hierarchy representation.</li>
<li>exportTraverse() assumes access validation has already occurred during tree construction.</li>
</ul>
<p>Suggested fix direction: include a hash of the current user's permissions or roles in the cache key (consistent with the user.permissions cache context used elsewhere in core), or re-run bookTreeCheckAccess on retrieval rather than caching the filtered result.</p>
<h3 id="summary-proposed-resolution">Proposed resolution</h3>
<p>TBD</p>
<h3 id="summary-remaining-tasks">Remaining tasks</h3>
<p>Fix<br>
Test coverage<br>
Commit</p>
<h3 id="summary-ui-changes">User interface changes</h3>
<p>NA</p>
<h3 id="summary-api-changes">API changes</h3>
<p>NA</p>
<h3 id="summary-data-model-changes">Data model changes</h3>
<p>NA</p>
<p>Credit from use @m4nh_thu4n, @catch, @larowlan, @greggles</p>
issue