Search code examples
javascriptgoogle-chromecontent-security-policynonce

Google Chrome Stripping nonce values from script tags


I'm trying to add nonce values to my inline scripts to satisfy a stricter CSP. However, I am running into a weird issue where chrome is stripping the value from nonce attributes. When I curl the page, nonce values are present. This is causing the script not to be executed as it now fails the CSP test. I thought this may be due to a rogue extension, but it fails on a perfectly clean version of chrome. (Version 73.0.3683.103 on OSX) The value for the nonce is a random 256 bit base encoded string, so it should satisfy all requirements for the nonce.

Does anyone have any idea what is going on? Am I doing something wrong?


Solution

  • What’s described in the question is actually expected behavior — required per the HTML spec:

    https://html.spec.whatwg.org/multipage/#nonce-attributes:attr-nonce

    Elements that have a nonce content attribute ensure that the crytographic nonce is only exposed to script (and not to side-channels like CSS attribute selectors) by extracting the value from the content attribute, moving it into an internal slot named [[CryptographicNonce]]

    https://html.spec.whatwg.org/multipage/#nonce-attributes:dom-noncedelement-nonce

    …the setter for the nonce IDL attribute does not update the corresponding content attribute. This, as well as the below setting of the nonce content attribute to the empty string when an element becomes browsing-context connected, is meant to prevent exfiltration of the nonce value through mechanisms that can easily read content attributes, such as selectors.

    This behavior was added in an update to the spec at https://github.com/whatwg/html/pull/2373 (Hide nonce content attribute values); see https://github.com/whatwg/html/issues/2369.

    To be clear: the behavior the spec requires is, if the markup source you serve over the wire has:

    <script nonce=DhcnhD3khTMePgXw>...</script>
    

    …then if you open browser devtools and use the DOM inspector, what you’ll see instead is this:

    <script nonce>...</script>
    

    That is, the DOM inspector will show no value for the nonce attribute on that script element.

    More accurately: you’ll see no value for the nonce attribute on that script if the doc is served with a Content-Security-Policy header, and the browser is applying the policy in that header.

    If you don’t serve the doc with a Content-Security-Policy header, or browsers don’t apply the policy from it, you’ll see nonce=DhcnhD3khTMePgXw for the script element in the inspector.

    So the lack of a value for that nonce attribute in the DOM inspector actually indicates that things are working as expected. That is, it indicates the browser is checking the value for a match against any nonce-* source expressions in the Content-Security-Policy header.

    The way it works inside browsers is: browsers move the nonce attribute’s value to an “internal slot” for the browser’s own use. So it stays available to the browser, but hidden from the DOM.


    https://wpt.fyi/results/content-security-policy/nonce-hiding shows that all current browser engines now conform to the spec on this.