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:
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' *;"
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