Given you have a strict CSP that only allows <script src>
elements from your own domain:
Content-Security-Policy: script-src 'self'
This will block JavaScript handlers inlined as attribute into your HTML elements. Clicking on the following link will only log an error with a strict CSP:
<a href="javascript:alert('hello')">click me</a>
<a href="#" onclick="alert('hello')">click me</a>
The recommended solution is to move the handler from the HTML to the allowed JavaScript file that we loaded via <script src>
.
In the example above we could invent a new [data-alert]
attribute with the alert message:
<a href="#" data-alert="hello">click me</a>
Then our JavaScript intercepts clicks on elements with that attribute:
document.addEventListener('click', function(event) {
let link = event.target.closest('[data-alert]')
if (link) {
let message = link.dataset.alert
alert(message)
event.preventDefault()
}
})
Some browsers
Show archive.org snapshot
allow the CSP directive
script-src-attr
Show archive.org snapshot
. This lets you allow the hashes of actual JavaScript code.
The SHA256 hash of alert('hello')
is vIsp2avtxDy0157AryO+jEJVpLdmka7PI7o7C4q5ABE=
(in Base64). We can allow this one event handlers like this:
Content-Security-Policy: script-src 'self'; script-src-attr 'unsafe-hashes' 'sha256-vIsp2avtxDy0157AryO+jEJVpLdmka7PI7o7C4q5ABE='
Note the sha256-
prefix.
This event handler now works when clicked:
<a href="#" onclick="alert('hello')">click me</a>
But any other script will still be blocked:
<a href="#" onclick="alert('hi')">click me</a>
Currently (November 2023) about 75% Show archive.org snapshot of browsers support script-src-attr.
Here is a forward-looking compromise that many users use with new CSP features:
The CSP spec supports that approach in that using newer, more specific directives disable older, more general features.
In our case this means:
Here is a CSP directive that works like this:
Content-Security-Policy: script-src 'self' 'unsafe-inline'; script-src-elem 'self'; script-src-attr 'unsafe-hashes' 'sha256-vIsp2avtxDy0157AryO+jEJVpLdmka7PI7o7C4q5ABE='
Old browsers will only use script-src
.
New browsers will use script-src-elem
(for <script>
tags) and script-src-attr
(for inline event handlers), which override the more liberal rules from script-src
.
You can have a secure web application without a strict CSP.
A Content Security Policy should never be your first line of defense against Cross-Site-Scripting (XSS). Your primary tool should be to escape and sanitize user input. Many template engines make it easy to do the right thing by escaping dynamic content by default.
A CSP is a second security net. It's there when you slip up and somehow, somewhere don't sanitize user input.
Depending on your project, you may decide to prioritize developer convenience over a strict CSP.