Click here to Skip to main content
14,035,510 members
Click here to Skip to main content
Add your own
alternative version

Tagged as


1 bookmarked
Posted 14 Apr 2019
Licenced CPOL

What Web Developers Need to Know About Content Security Policy - Part 2

, 14 Apr 2019
Rate this:
Please Sign up or sign in to vote.
This article continues the content security policy discussion with unsafe-inline, unsafe-eval, nonce, cryptographic hashing and more.


In Part 1 of the article, we looked at how whitelisting trusted source can prevent the malicious code on untrusted site from executing. In this second part of the article, we delve into use of unsafe-inline, unsafe-eval keywords in CSP 1 and together with nonce and hashing, introduced in CSP 2, to refrain from ever using unsafe-inline by enabling only trusted inline code to execute. Please note that keywords like unsafe-inline and unsafe-eval have to be enclosed in single quotes.


Sometimes, the webpage has come with some inline JavaScript or stylesheet and for enormous amount of work involved, it is not feasible to externalize them in a separate file. This is where unsafe-inline comes into the picture. For this example, we have an HTML that displays time periodically.


<title>unsafe-line in Action</title>


<p id="time"></p>

<script type="text/javascript">
function displayTime()
    var d = new Date();
    var n = d.toLocaleTimeString();
    document.getElementById('time').innerHTML = n;
    setTimeout(function () {
        }, 500);



Copy the HTML and save it in an HTML file. And open to view that HTML on web browser. We can see that time is displayed. Your time, most likely, is different from mine. Let's add a CSP <meta> tag in the <head> section.

6:32:32 PM

<meta http-equiv="Content-Security-Policy" content="default-src 'self';">

Bam! Time does not display and now we have an error. This is the error I got on Chrome.

Refused to execute inline script because it violates the following Content Security Policy directive: "default-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-TVjy1frkE+v+8vB4X884wNJ7xy5bKc32l3WYqLZZ44o='), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.

Let's enable our inline code with unsafe-inline.

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; 
      script-src 'unsafe-inline';">

This time around, the HTML displays the time.

6:35:32 PM

Nonce and Hash to the Rescue

unsafe-inline is an all or nothing solution which leaves much to be desired. When unsafe-inline is enabled, there is a risk that we are also enabling maliciously injected code.

nonce and hashing are introduced in CSP 2 to address this gaping security hole exposed by unsafe-inline. How they work, is they are enabling JavaScript or CSS section with the same nonce value or correct cryptographic hash to execute. Nonce and hash have to be enclosed in single quotes. Remember nonce is used-only-once base64 encoded number that needs to be updated on every page fetch. As long as the nonce in CSP and script/style section matches, the JavaScript or CSS is allowed. Below are the script-src nonce and style-src nonce examples.

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; 
      script-src 'nonce-2726c7f26c';">

<script nonce="2726c7f26c">
// code remains unchanged, so it is not shown.
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; 
      style-src 'nonce-5823c7f85c';">

<style nonce="5823c7f85c">
// CSS code not shown

Cryptographic hashing works by calculating the cryptographic message digest of inline code inclusive of their whitespaces and then encoded the hash in base64 format. For a Chrome user, you are lucky because Chrome calculates this hash for you when showing the error on the developer console. I reproduce the above error here again.

Refused to execute inline script because it violates the following Content Security Policy directive: "default-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-TVjy1frkE+v+8vB4X884wNJ7xy5bKc32l3WYqLZZ44o='), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.

All you need to do is to fix the error is to copy SHA256 hash to the script-src directive.

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; 
      script-src 'sha256-TVjy1frkE+v+8vB4X884wNJ7xy5bKc32l3WYqLZZ44o=';">

// code remains unchanged, so it is not shown.

The same concept works for style section.

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; 
      style-src 'sha256-pkvqLyskjufPOv5VOGnLcoqyD2oDwsfaPxxvXCQdq9Y=';">

// CSS code not shown

Viola, the error is gone and time display is back!

Nonce versus Cryptographic Hash

Given the choice between nonce and cryptographic hash, what would be the preferred approach? For the latter, hash has to be recalculated whenever the code is updated while the former requires a carefully-designed random nonce generation policy to ensure the nonce is not easily guessable.

Cryptographic Hashing for External JS and CSS

The same cryptographic hashing approach can be done for external JavaScript and CSS file with Subresource Integrity (SRI). Subresource Integrity is a security feature that enables browsers to verify that fetched resources (for example, from a CDN) are delivered without modification. To use SRI, just compute hash and encode the hash in base64 format and add in under integrity attribute of the script or style tag. And remember to enable the require-sri-for directive for JS or CSS respectively as shown below:

Content-Security-Policy: require-sri-for script;

<script src=""


Content-Security-Policy: require-sri-for style;

<link href="" rel="stylesheet" type="text/css" 



When the script or stylesheet doesn’t match its integrity value, the browser shall refuse to execute the script or apply the stylesheet.


Sometimes, a legacy library cannot be easily modified and is using eval() to dynamically generate JavaScript code. In this case, the resolution is either if feasible, a library replacement or, as a last resort, allowing of dynamic JavaScript code through unsafe-eval keyword.

Clickjacking Prevention

Clickjacking is a malicious technique of tricking a user into clicking on something different (usually invisible) from what the user can see. For instance, a web page is overlapped with an iframe whose opacity set to zero, when the user clicks a legitimate link, unbeknownst to him, he is clicking a link or button on that invisible iframe. CSP 2 introduces frame-ancestors directive to whitelist URL(s) that is permitted to embed your webpage.

Upgrade Requests from HTTP to HTTPS

By setting the upgrade-insecure-requests directive, web browser is instructed to fetch all resources using HTTPS scheme. Another directive, block-all-mixed-content forbids loading any assets using HTTP when the page is loaded using HTTPS. In practice, you only need to set either upgrade-insecure-requests or block-all-mixed-content but not both.

Zero Risk CSP: Report-Only

There is an inherent risk in CSP whitelisting approach where a legitimate source of content is overlooked and omitted in CSP, causing some functionality to break. This is simply unacceptable. In CSP 2, enforcement can be turned off and switched to report-only mode by renaming Content-Security-Policy to Content-Security-Policy-Report-Only and remember to add report-uri and report-to directive for report destination. Note that report-uri and report-to can also be added to normal violation blocking Content-Security-Policy as well.

Why is there a need to specify 2 directives that point to the same report destination? To keep the long story short, report-uri has been renamed to report-to in CSP 3 but at the time of article writing, no web browser supports report-to directive yet. To future-proof your CSP, it is better to specify report-to in addition to report-uri. A point for developer to note is these report directives are not supported in the <meta> element, meaning it has to be specified in the CSP response header. Violation report is sent in JSON format by HTTP POST method. Whenever there is a violation report, it could mean one of the two things, a trusted source is not whitelisted or the webpage is having XSS attacks.


In this final instalment of the 2-part article, we have looked at unsafe-inline and unsafe-eval to enabling inline code and dynamic code generation. And we also looked at how nonce and cryptographic hashing help developers to run only their own code. We briefly looked at clickjacking prevention, upgrading request to HTTPS and report-only CSP where CSP is not enforced.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Shao Voon Wong
Software Developer (Senior)
Singapore Singapore
Shao Voon is from Singapore. His CodeProject contributions could range from being used in game changer Large Hadron Collider (LHC) of CERN to smash particles to something as frivolous as Maplestory game to as life changing as electrocardiography(ECG) project to everyday indispensable Irfanview image viewer.

CodeProject bestowed him a MVP award in recognition of his article contributions in 2019. In his spare time, he prefers to writing application based on 3rd party library than writing his own open source library. His interest lies primarily in computer graphics, software optimization, security and Agile methodologies.

One of these days, you can find him working on a DirectX based slideshow app called Mandy Frenzy.

You can reach him by sending a message on CodeProject or at his Coding Tidbit Blog!

You may also be interested in...

Comments and Discussions

-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web03 | 2.8.190424.1 | Last Updated 15 Apr 2019
Article Copyright 2019 by Shao Voon Wong
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid