Removing Inline Resources

Content-Security-Policy blocks all inline resources. This is because it is impossible for the browser to determine what inline resource was intentional, and what resource wasn't intentional. So it just blocks everything. (If we could tell what was intentional and what wasn't, we wouldn't have XSS in the first place).

But, it's good that content-security-policy is so picky about inline content, that's how it provides so much value in terms of security.

What is an inline resource?

Here's a couple of examples:

<script>alert(1)</script>
<button id="aBtn" onClick="alert(1)">Click Me </button>
<style>
  body { background-color: red;}
  body::before { content: "Virus detected! Pay bitcoin to 32wr932jf2" }
</style>

Above is an inline script, style, and event handler. All of them would be blocked by Content-Security-Policy.

How To Migrate Inline Scripts

The simplest way is to move them to their own javascript/style files and make sure you have 'self' whitelisted on your policy.

So instead the above would be:

app.js

alert(1)
document.getElementById("aBtn").addEventListener('click', function() {
  alert(1)
});

app.css

body {
  background-color: red;
}
body::before {
  content: "Virus detected! Pay bitcoin to 32wr932jf2"
}

Can I use 'unsafe-inline'?

For script-src, no. Not if you're trying to protect your users.

For style-src, maybe, depends on your security stance. Including 'unsafe-inline' on style-src means that if an attacker can find an injection point than can inject arbitrary CSS. The attacker could deface or make the UI confusing.

For more information on why 'unsafe-inline' is bad check out this blog post: https://csper.io/blog/no-more-unsafe-inline​

What if I can't remove all the inline content?

One other option is to run Content-Security-Policy in Report-Only mode, and just monitor for naughtiness.

The reports will still be fired for all the existing inline content, but also for any XSS's. So you can filter out the known inlines (using a tool like https://csper.io) and just look for new inlines. Using 'script-sample' you can also see the first 40 characters to somewhat determine if it was malicious.

It's still highly recommended to instead focus on removing all inline content though.