You know what I really like? A nice, slick, clean set of violation reports from the content security policy (CSP) I run on Have I Been Pwned (HIBP). You know what I really don't like? Logging on to Report URI and being greeted with something like this:
This blog post is about how add-ons and extensions in browsers cause CSP violations like the ones above and how they should be dealt with. Some brief background first as I'll be sharing this post with a bunch of folks for which this may be new: A CSP is a response header or meta tag that allows you to declare a policy for your website declaring what sorts of content can be loaded from where. For example, on HIBP I declare that scripts can be loaded from the same site and also from Cloudflare's CDN because I pull jQuery from their public library. I also declare that forms can post back to the HIBP website and also to PayPal because that's how the donation page works. If, for example, someone manages to find a cross site scripting vulnerability on HIBP and uses that to attempt to load in a script from another host or post a form somewhere not already whitelisted, the browser honours the CSP and blocks it. That's not an exhaustive description of what you can do with the technology, but it's a good single paragraph primer.
Now here's the cool bit: when a violation occurs, you can instruct the user's browser to send you a report which is a JSON payload explaining what went wrong. You can send that report to whatever address you like via the "report-uri" directive in the policy. The Report URI website I mentioned earlier is run by Scott Helme and myself and it enables website operators to submit their reports to the service then view them in one centralised location with some neat features around things like graphing and filtering. As of today, we process more than 10 billion reports a month (yes, with a "b"). That number is changing rapidly too because back in August when Scott last ran the numbers, only 3.5% of the world's top 1 million websites had a CSP (and only a subset of those are reporting), but that number is growing at more than 40% every six months. The growth - and the future potential - is massive.
Getting back to the earlier image full of violations, let's have a look at the raw CSP report that's coming through and I'll take the first one in the list that's violating the font-src directive on the homepage:
{
"csp-report": {
"blocked-uri": "https://cdn.honey.io",
"document-uri": "https://haveibeenpwned.com/",
"original-policy": "default-src 'none'; script-src https://haveibeenpwned.com 'nonce-[removed]' https://www.google-analytics.com https://www.google.com https://www.gstatic.com https://cdnjs.cloudflare.com https://az416426.vo.msecnd.net; style-src https://haveibeenpwned.com 'unsafe-inline' https://cdnjs.cloudflare.com; img-src https://haveibeenpwned.com https://www.google-analytics.com https://stats.g.doubleclick.net https://www.gstatic.com; font-src https://haveibeenpwned.com https://cdnjs.cloudflare.com https://fonts.gstatic.com; connect-src https://haveibeenpwned.com https://api.pwnedpasswords.com https://api.haveibeenpwned.com https://www.google-analytics.com https://stats.g.doubleclick.net https://dc.services.visualstudio.com; base-uri https://haveibeenpwned.com; child-src https://www.google.com; form-action https://haveibeenpwned.com https://accounts.google.com https://www.paypal.com; frame-ancestors 'none'; upgrade-insecure-requests; report-uri https://troyhunt.report-uri.com/r/d/csp/enforce",
"violated-directive": "font-src"
}
}
This is precisely what the users' browsers have sent us - this is what you get in a CSP violation report. Let's break down what we can learn from this in the order in which the JSON is formatted:
- blocked-uri: Something is attempting to load content from https://cdn.honey.io
- document-uri: It's attempting to load it into the DOM when someone visits the home page of HIBP (you can see from the earlier image that this is also happening on other pages on HIBP, as well as on my blog)
- original-policy: This is the policy it violated, in other words it's the one that loaded into the visitors' browsers and blocked the external content from being loaded
- violated-directive: The thing that was attempted to be loaded was a font
So in short, people are coming to my websites and something is trying to embed a font from cdn.honey.io into the pages. Not shown in the report itself but passed in the request headers is the user agent and per that earlier image, it's Firefox all the way. It only takes a couple of minutes to locate the add-on responsible for this - it's Honey:
Evidently, Honey is a pretty popular add on. A bit of sleuthing explains why:
Honey is a free browser extension that automatically finds and applies coupon codes at checkout for over 30,000 shopping sites.
Right, so run Honey in the browser and save money. It's so popular, that apparently over 9 million people are already on board:
Because I wanted to reproduce these violations, I went ahead and installed the add-on into Firefox. As part of the installation process, I want to highlight the permissions Honey is requesting:
I've said it before and I'll say it again: when you install an add-on, you're giving it an enormous amount of power over your browser. You really really want to consider which add-ons you run in Firefox (or extensions you run in Chrome) because being able to "access your data for all websites" is major. You'll find countless stories from this year alone about add-ons and extensions going rogue, something I've highlighted time and time again:
Do you use a popular browser extension? How confident are you that the creator wouldn’t accept a $10k offer to hand it over only to have it then go rogue on you? https://t.co/hPfW5CJLUz
— Troy Hunt (@troyhunt) September 5, 2018
I was only just recently talking about how much power browser extensions have, you really want to double-think just how much you need each one https://t.co/BFwXVon3Uo
— Troy Hunt (@troyhunt) November 3, 2018
That said, I'm going to trust an extension from the likes of Honey or 1Password or Microsoft a lot more than I am one where, like in the earlier tweet, it might be sold out to nefarious parties. So I install it and am immediately taken to the signup page. However...
And why is this happening? Because they're attempting to load what appears to be a 1x1 transparent pixel (this sort of thing is often used for tracking purposes), over the insecure scheme:
As it turns out, the very thing that drew me to Honey in the first place (content security policies) can very easily fix this problem via the upgrade-insecure-requests directive. Even better, you can report on these violations too and there's a demo page that shows precisely how to do this (look at the response headers, HTML source and network requests).
With the extension now installed, you can now see the Honey add-on represented in the top-right corner of the browser. So, it's over to HIBP to see what it's trying to do:
Ah. Yeah, that'd do it. And sure enough, that then leads to 4 violation reports being sent off to my Report URI account:
But why? What possible reason could Honey have for attempting to insert fonts into HIBP? And what should we do at Report URI to deal with this? Plenty of people had raised the presence of Honey in their reports with Scott and I, what was the right action? We discussed two alternatives:
The first one was to just filter Honey out of the reports. We already filter all sorts of unnecessary noise and benign reports, it'd be simple to add Honey to the list. But that wouldn't really fix the underlying cause of the reports; Honey would still be trying to inject fonts into the DOM and, assuming they were actually necessary to the function of Honey, the CSP would be actually be breaking the add-on. And besides, it wasn't just Honey, there were plenty of other add-ons out there causing CSP violations to be generated.
The second alternative was to appeal to Honey to whitelist themselves in the site's CSP if one exists. Remember when I said add-ons have an enormous amount of power? That extends to modifying response headers too so in an ideal world, Honey would see a CSP, see a font-src directive then just simply add cdn.honey.io. Their fonts would then load and their extension would function correctly, no CSP would be violated and subsequently, no report issued. Everyone goes homes happy!
So, earlier this week I reached out via Twitter and found a contact at Honey. They were super-awesome in the way they responded, understanding and acknowledging the issue with fonts being loaded. As it turns out, the loading of fonts wasn't intentional and this behaviour had already been removed from the Chrome extension last month. The Firefox add-on with the fonts removed was already in QA so sometime very soon, people should see Honey drop off their CSP violation reports. They also acknowledged the need to whitelist themselves should they later on need to inject other content into a page with a CSP which is precisely what we'd like to get out of other add-on developers. Kudos to Honey all round for the way they responded on this!
As I look down my list of CSP violations now, I see many other instances of add-ons causing similar behaviour. There's violations caused by RiteKit (a social media add-on), UCBrowser (a browser built by an Alibaba company) and Ciuvo (a deal-finder), among others. So here is our ask of these developers and all the others injecting content into the DOM:
If an extension or add-on injects images, fonts, scripts, iframes, media or makes any other changes that might possibly violate a CSP, it should whitelist itself. Do this not just to cut out the noise it's otherwise generating, do this as a simple matter of self-preservation as well.
And if there's any developers out there with remaining questions on how to do this, drop them into the comments below and Scott or myself will gladly help out. Thank you!