Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strict-Dynamic CSP doesn't prevent execution of parser inserted scripts via document.createRange().createContextualFragment #708

Open
andreituicu opened this issue Feb 26, 2025 · 0 comments

Comments

@andreituicu
Copy link

Problem

strict-dynamic Content Security Policy doesn't prevent execution of parser inserted scripts via document.createRange().createContextualFragment.

This behaviour seems unexpected and can be an easy target to XSS and CSP bypass, as a number of developers are using document.createRange().createContextualFragment as an interchangeable alternative to Element.innerHTML, Element.outerHTML, Element.insertAdjacentHTML to transform strings into DOM.
Not only does the script get executed without the CSP, as opposed to the behaviour of the other properties, but it is also executed even if strict-dynamic is present, which came as a surprise, because intuitively it looks like a parser injected script: A string is directly converted into a script, without the step of invoking document.createElement("script") separately.

Steps to reproduce

  1. Create an HTML with content security policy strict-dynamic + nonce.
  2. Create a trusted script with the nonce
  3. In the trusted script, invoke document.createRange().createContextualFragment('<script>alert("executed")</script>')
  4. Add the result of the previous execution to the dom, in the head

(see below example HTML + JS)

Result

The script is executed.

Expected result

The script should not be executed, because it comes from a string that is parsed. Even if the nonce is set I would expect the string to not be executed.

Example HTML + JS

index.html

<html>
  <head>
    <meta http-equiv="Content-Security-Policy" content="script-src 'nonce-r4Nd0mmm' 'strict-dynamic'">
    <script nonce="r4Nd0mmm" src="./script.js"></script>
  </head>
  <body>
    <p>Content Security Policy <strong>nonce</strong> + <strong>strict-dynamic</strong> doesn't prevent parsed scripts from <strong>document.createRange().createContextualFragment()</strong></p>
  </body>
</html>

script.js

// Should not be executed because it's parser inserted
document.head.appendChild(
  document.createRange().createContextualFragment('<script>alert("executed")</script>'),
);

// Should also not be executed because it is still parser inserted, even if it has the valid nonce
document.head.appendChild(
  document.createRange().createContextualFragment('<script nonce="r4Nd0mmm">alert("executed with nonce")</script>'),
);

CC @lkrapf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant