0
votes

While hardening my CSP by removing all unsafe-inline scripts by giving them a nonce, I ran into a problem on a particular page of my website. I edited everything to make it more easily understood on a small scale scenario.

product.php loads the template for which the product will be displayed.

<div id="productDisplay"></div>
<script nonce="sampleCorrectNonce" type="text/javascript">
    product();
</script>

product.php?id=123 loads the actual product data and needs to be refreshed with new information through different scenarios.

if ($_GET['action'] === 'displayProduct') {
    echo '
    samleData
    <script nonce="sampleCorrectNonce" type="text/javascript">
        productReady();
        productOptions();
    </script>
    ';
}

On product.php there is an initial javascript code that fires and loads product.php?id=123 into an DIV by ID.

product = function() {
    $("#productDisplay").load("//"+ document.domain + "/shop/product.php?action=displayProduct&id=123");
}

A nonce has been created to allow CSP on inline scripts. Despite the fact that the nonce matches exactly and the < script > is presented inline on the same page, it still triggers an error.

<html>
    <body>
        <div id="productTemplateStuffa"></div>
        <div id="productDisplay">
        samleData
        <script nonce="sampleCorrectNonce" type="text/javascript">
            productReady();
            productOptions();
        </script>
        </div>
        <div id="productTemplateStuffb"></div>
        <script nonce="sampleCorrectNonce" type="text/javascript">
           product();
        </script>
    </body>
</html>

For some reason, I cannot figure out how to include PHP echoed content from jQuery's load into a DIV while keeping CSP happy.

Firefox's Console Error:

Content Security Policy: The page’s settings blocked the loading of a resource at self (“script-src https://example.loc/ 'nonce-sampleCorrectNonce' 'unsafe-inline' 'unsafe-eval'”). Source: productReady(); ....

This is essentially what the header is sending, I edited it down to keep it simple and hide all external content.

Content-Security-Policy: base-uri 'self'; default-src 'self'; connect-src 'self'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' data:; media-src 'none'; object-src 'none'; script-src 'self' https://example.loc/ 'nonce-sampleCorrectNonce' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; upgrade-insecure-requests

Removing this fixes the error...

<script nonce="sampleCorrectNonce" type="text/javascript">
    productReady();
    productOptions();
</script>

Doing this, does NOT fix the error...

<script nonce="sampleCorrectNonce" type="text/javascript">
</script>

This is how Chrome, above, and Firefox, below, shows the inline script content.

<script nonce="" type="text/javascript" src="https://example.loc/theme/assets/global.jquery.min.js?v=1540058004"></script>

<script nonce="sampleCorrectNonce" type="text/javascript" src="//example.loc/theme/assets/global.jquery.min.js?v=1540058004"></script>

Deleting everything inside the script tag, still triggers the error. OK, since it's triggering a GET method, it's setting new headers with a new nonce. If I didn't have the script tags on the GET page, it wouldn't be a problem. I have an unsolvable problem, since the content is dynamic, a hash won't even work.

Doing this on the GET page, allows the headers to match the nonce from the GET request but now viewing source shows mismatched nonces, expectedly. Which means the CSP will trigger still.

<script nonce="' . Headers::getInstance()->getScriptHash() . '" type="text/javascript">

I apologize for the length of this post. I solved the problem by simply rewriting the code so there is no inline script on the GET request. This is what you should be doing anyway and is probably why they designed CSP to work this way.

1
Can you please post the whole error message that the chrome console displays regarding the CSP violation. Can you also post your full CSP header, please!markus
Is it possible that the javascript code that loads product.php has a nonce from another roundtrip? Did you check if all your nonces in your source are identical with the nonce in your header at any given client side moment?markus
At first, the dynamic content from product.php?id=123 was being given a second nonce, I thought this was the problem, so I passed the nonce through the product() javascript function. The nonce's matched as viewed in Inspector in Firefox after I did that. So a matching nonce isn't the problem. I'll post the complete CSP in an edit above.ParadiseStudios
Ok, now that's too much material for one question and why did you answer your own question as if you knew the answer?markus

1 Answers

0
votes

If all the rendered markup contains the same nonce(s) as your CSP header, it will work. So there is a possibility that not all the rendered nonces are part of your header. Since you mentioned in a comment that you think that should not be the problem, it can be assumed that you have other inline scripts still present in your markup.

Here's what Chrome says:

Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.

Firefox should say something like this:

Content Security Policy: Ignoring "'unsafe-inline'" within script-src: nonce-source or hash-source specified

Since you obviously have a nonce-x in your source list, the unsafe-inline directive will be ignored. Since you still have that directive in there I can only assume you still have other inline scripts that don't use the nonce which probably explains your error.