0

I have a site in PHP and I use Mustache for the templates. Inside those Mustache templates I have javascript that would need to be executed once the template has been loaded. I also have some external scripts from google (ex: google charts) or facebook (ex: facebook pixel) in there so I need to be able to run those too.

I can execute those scripts with eval(), it works but I would like to use nonce to secure them but I can't figure out how to make this work.

What I do in PHP to create the nonce:

$nonce = bin2hex(random_bytes(16));
session_start();
$_SESSION['CSP_NONCE'] = $nonce;

What I do in PHP to pass the nonce to Mustache:

$template = $mustache->loadTemplate('comments');
echo $template->render(array('translations'=>$translations,'nonce'=>$nonce));               

Inside the template:

<script nonce="{{nonce}}">

I call my template with fetch and check if there is a script inside to execute it (element contains the content of the Mustache template:

var scripts = document.getElementById(element).querySelectorAll("script");
for (var i = 0; i < scripts.length; i++) 
    {
        if (scripts[i].innerText) 
        {
            eval(scripts[i].innerText);
        } 
    }

I also tried this:

var scriptTags = document.getElementById(element).querySelectorAll("script");

for (var i = 0; i < scriptTags.length; i++) 
    {
    var scriptContent = scriptTags[i].textContent || scriptTags[i].innerText || scriptTags[i].innerHTML;
        if (scriptContent.trim() !== "") 
            {
                var scriptFunction = new Function(scriptContent);
                scriptFunction();
            }
    }

When looking at the source code, I do see the nonce with each but execution is blocked by CSP.

I tried two different scenarios to solve the CSP issue:

  1. Through .htaccess

    Header set Content-Security-Policy "default-src 'self' .laurentwillen.be .laurentwillen.com .laurentwillen.nl .youtube.com https://.google-analytics.com/; script-src 'self' 'nonce-%{ENV:CSP_NONCE}' 'strict-dynamic' https://.ytimg.com https://.googleapis.com https://.googletagmanager.com https://www.google-analytics.com/ https://www.google.com https://cse.google.com https://.gstatic.com https://.facebook.net/ https://*.facebook.com/ https://www.youtube.com; style-src 'self' 'unsafe-inline' *.gstatic.com; img-src 'self' *; font-src 'self' *;"

  2. Through PHP by setting headers

    header("Content-Security-Policy: default-src 'self' .laurentwillen.be .laurentwillen.com .laurentwillen.nl; script-src 'self' 'nonce-$nonce' 'strict-dynamic' https://.ytimg.com https://.googleapis.com https://.googletagmanager.com https://www.google-analytics.com/ https://www.google.com https://cse.google.com https://.gstatic.com https://.facebook.net/ https://*.facebook.com/ https://www.youtube.com *.laurentwillen.be *.laurentwillen.com *.laurentwillen.nl; style-src 'self' 'unsafe-inline' *.gstatic.com; img-src 'self' *; font-src 'self' *;");

With option 1, I always had a http 500 error code. With the second option, pages where loaded, scripts too except for the scripts that were called through onXXXX like onchange="myfunction()", those ones where still blocked

I'm not a CSP specialist and I don't see if there is an issue there, the rest seems fine to me.

Any idea?

Thanks

Laurent
  • 1,465
  • 2
  • 18
  • 41

1 Answers1

0

For your first option 500 is a server error, so something is apparently going wrong when processing your .htaccess file. Check your logs. But anyway, will your nonce change on every pageload with this approach as it should?

Inline script attributes are not nonceable, this only applies to tags. You will need to rewrite using event listeners instead.

Halvor Sakshaug
  • 2,583
  • 1
  • 6
  • 9
  • I have checked my server logs but couldn't find any error message related to a problem with htaccess file. I went back to the PHP approach and yes, I generated a new token for every page load. Do you mean that integrating the nonce in – Laurent Aug 11 '23 at 10:47
  • It will work in – Halvor Sakshaug Aug 11 '23 at 11:50