While building my blog in Drupal 8, I noticed some changes to the way Drupal Settings were implemented. Instead of defining drupalSettings in a JavaScript block, they are now being defined as a JSON object in an application/json block. This is intended to pass data from PHP to JavaScript, while honoring the content security policy principle which no longer allows inline scripting - or at least advises strongly against it.
Content security policies are becoming an important tool for web developers as a deterrent to cross site scripting attacks. I was first introduced to them when our IBM AppScan findings started to return low level infractions because we did not include policies in our clients' web responses.
Content security policies are becoming an important tool for web developers as a deterrent to cross site scripting attacks.
Passing data from the server application layer to JavaScript is a fairly common task, so having to work around this for CSP compliance will pose a challenge for many existing sites. Even when you follow best practices and import your JavaScript through external files, there are many use cases where JavaScript needs information about the current request that it cannot get otherwise, for example session data. One way to work around the need for inline JavaScript is to use Ajax calls, although this does not fully solve the problem because it is done asynchronously and uses a different request.
Workarounds
If you absolutely must have inline scripts, the content security policy spec allows for the inclusion of inline scripts if they are registered in the policy header using a unique key, or "nonce". The key can be declared in your policy, should be renewed with each request, and is then added to the script tag using a nonce
attribute.
But if even that is too much to implement, for example if you're managing a site with tons of legacy inline script usage that you don't think will be changing anytime soon, you can override the policy entirely by using the 'unsafe-inline' as a script source in your policy.
The solution provided in Drupal 8 for defining drupalSettings is simple and elegant, and can be used for other application variables. In your html output, you can declare a block using application/json
instead of application/javascript
.
<script type="application/json" data-selector="my-data">
{"data":{"test":"one"}}
</script>
Then, in your imported javascript file (using jQuery in this example), you can call the JSON data into scope:
var sample = JSON.parse($('script[data-selector="my-data"]').html());
window.alert(sample.data.test); // returns 'one'