CSP implementations are broken


  • frame-src is inconsistent cross browser
  • block-all-mixed-content is broken in Chrome and Opera
  • CSP reports are inconsitent
  • Edge has some weird edge cases (no pun intended)


There has been a lot of talk lately about Content Security Policy (CSP) after an accessibility script called BrowseAloud got infected by a cryptominer and force the users of a couple of thousand websites to mine cryptocurrency without their knowledge. Content Security Policy could have prevented this issue as it contains rules for what the browser can load and what not to load.
Read more at https://content-security-policy.com

I recently held a talk with the title “Content Security Policy - Or how we ruined our site, learned a lesson, broke the site again and then fixed it”. This talk was based on my work at my current client. This post is sort of a summary of that talk and will outline some of the issues we found in different browsers and with different combinations of devices, OSs, browsers, extensions and whatnot.

Some info on the system we are building

My client provides payment services for e-commerce. The system will be loaded as an iframe on the e-commerce site and allows the customer to finish their purchase. We use features in CSP that require us to use CSP2 (e.g. script hashes).

Our system in turn loads an iframe from a trusted service provider (let’s call it SystemX). SystemX will in some cases redirect to one of their trusted providers. SystemX has literaly hundreds of trusted providers all over the world and each of these have their own page that must be loaded in the iframe. I will not go into more details on why to not reveal to much information about my client.

frame-src is inconsistent cross browser

If your CSP contains a frame-src that does not contain mailto: or tel: these links will be blocked inside the iframe except in Firefox and Edge. Firefox will open both links and Edge will open the mailto link but block the tel link.

I’m not really sure if it’s broken in Firefox or in the other browsers. There are valid arguments for both cases.

Workaround: Add mailto: and tel: to your CSP:

frame-src 'self' mailto: tel:

I have reported this to Microsoft but have not heard back.

Affected browsers: Firefox and Edge or all others depending on your point of view

Proof of concept: https://jellyhive.github.io/CspImplementationsAreBroken/mailto-and-tel-links-frame-src/

Edge and custom error pages

We load an iframe from a trusted service provider which in turn redirects to different sites depending on circumstances. As we cannot know what URLs will be redirected to we currenty use this frame-src in our CSP:

frame-src 'self' data: https:

The issue with Edge is that it will load custom error pages for issues such as DNS errors, SmartScreen blocking and error responses from the server (e.g. 400, 404, 500 etc). The error page is loaded via a ms-appx-web:// url (e.g ms-appx-web:///assets/errorpages/http_500.htm) which is blocked by the CSP and a blank page is displayed to the user. The result is that our service provider’s iframe is just blank if an error occurrs.

I have reported this issue to Microsoft in early March but have not heard anything back from them.

Workaround: Add ms-appx-web: to our frame-src:

frame-src 'self' data: https: ms-appx-web:

Affected browsers: Edge

Proof of concept: https:/jellyhive.github.io/CspImplementationsAreBroken/edge-ms-appx-web-frame-src/

Edge and extensions

Extensions installed in Edge are subject to the current page’s content security policy. Basically all installed extensions that try to do anything from loading images to JS will fail and a CSP violation will be logged.

According to the CSP spec this is wrong. The issue has been fixed but not yet released according to the Edge issue tracker (issue 1132012).

Affected browsers: Edge

If you serve your site using HTTPS and use the block-all-mixed-content directive in your CSP, mailto and tel links will be blocked inside iframes but not on your main page. This does not happen if you serve the site using HTTP.

If the user tries to click a mailto or tel link on your page (i.e. the parent page) it will work as intended. Clicking the same links in an iframe will log one of these two errors:

Mixed Content: The page at 'https://...' was loaded over HTTPS, 
but requested an insecure resource 'mailto:...'. This request
has been blocked; the content must be served over HTTPS.
Mixed Content: The page at 'https://...' was loaded over HTTPS, 
but requested an insecure resource 'tel:...'. This request
has been blocked; the content must be served over HTTPS.

This issue has been reported to Google and Opera. Opera has not yet responded.

Workaround: Remove block-all-mixed-content from your CSP (possibly use upgrade-insecure-requests instead)

Affected browsers: Chrome and Opera

Proof of concept: https://jellyhive.github.io/CspImplementationsAreBroken/mailto-and-tel-link-block-all-mixed-content/

Safari on older iOS devices does not support CSP2

“Older” in this case meaning iOS 9 or earlier. Safari on iOS 10 and 11 do support CSP2. Since we require the use of script hashes we also require CSP2.

Desktop Safari is also affected is not as big of a problem as most desktops are up to date. Current usage on our site is less than 0.9% for older Safari on desktop.

Workaround: There is no way to make this work so we have disabled CSP for older iOS devices using user agent sniffing.
Use 'unsafe-inline' together with script hashes in your script-src directive. Modern browsers will ignore 'unsafe-inline' if they find a script hash. Older browsers will not know what to do with the script hashes but will instead find the 'unsafe-inline' directive and allow inline scripts.

Affected browsers: Safari on iOS < 10 (both iPhone and iPad) and Safari 9 or earlier on desktop

Internet Explorer 11 only supports X-Content-Security-Policy and CSP1

IE11 supports CSP1 using the X-Content-Security-Policy. If you wish to support IE11 you need to either do some user agent sniffing and change the header from Content-Security-Policy to X-Content-Security-Policy or send out both headers for everyone.

In our case we barely have any customers on IE11 so we just send out the regular Content-Security-Policy header which is then ignored by IE11.

Affected browsers: Internet Explorer 11 (older versions does not support CSP)

CSP reports differ between browsers

The reports sent to your report-uri should follow a common standard defined in the CSP spec but browsers differ on what data they send.

  • Some versions of Safari includes the entire CSP in the violated-directive property. This is like saying “Something went wrong. You find out what and deal with it.”
  • Chrome on Android does sometimes not provide a blocked-uri when the violated-directive is frame-src. This means that we have no way of knowing what URL was blocked in the iframe.
  • Most browsers does not provide a script-sample when an inline script is blocked. script-sample is very helpful in debugging what script was blocked.

CSP reports contain lots of false positives

This is primarily due to browser extensions. Most extension work by injecting code on the page and code on the page is subject to the page’s CSP. A common issue we have found in our logs is

violated-directive: script-src
blocked-uri: about:blank

which is casued by adblockers when they replace the loading of tracking scripts (e.g. Google Analytics) with the loading of about:blank.


Content Security Policy is a great tool that should be deployed in more places. It does however take some fine tuning to make it work properly on a specific site.