I don't trust your browser

Lately I've been analyzing a huge CSP-report file and most of the reports are actually due to browser behaviors such as extensions or some other form of MITM - like proxies. These inject scripts or style into the website which then is blocked by my Content Security Policy (CSP). This led me to think what other kinds of stuff some browsers, extensions and/or proxies do with my website.

Note that a fight against a browser is practically impossible to win and these methods are nothing more than a second layer of security that only may help. However, many (most) of the extensions don't know about these methods so they don't have a workaround. I would still say that these methods are highly recommended and should be considered as best practice.

A "funny" side note is that many reports triggered by browser extensions that I've seen are actually on the login page of the website. This led me to creating dynamic CSP and using no scripts at all on the login-page, which indeed increased the amount of reports.

First things first

The absolute first layer of protection you should have is CSP and HTTPS. When I say HTTPS I always mean correctly configured HTTPS - this means preload-HSTS, strong ciphersuites, OCSP-stapling etc.

The CSP should be in enforced mode and be rather strict. Avoid unsafe-* because otherwise you need to deal with nonce and if you don't do that you've already lost.

SRI to the rescue

Subresource Integrity (SRI) is something very useful and as the name states, it should be used for sub resources. But there's nothing that prevents us from using SRI-hashes on locally served CSS,- and Js-files.

For example, let's assume that your CSP is script-src 'self', a browser or an extension could just inject Javascript into the files served from the domain and the CSP would still be valid because the content is still served from self. And here's where SRI comes in handy! If we explicitly tell the browser that the provided hash must match the files content the above method would not work.

SRI is supported in Chrome and Firefox so start using it today if you haven't already! Getting started is easy, you can use the online tool over at report-uri.io and then include the integrity - attribute in your <link rel="stylesheet"> and <script> - tags.

Remember to add require-sri-for script style to your CSP if you always use SRI (hint: you should).

But - of course - if a browser already can modify the content, it can also modify (remove or change) the integrity and bypass it completely. But most (if not all) extensions does not do this.

CSP in meta-tags as "fallback"

It has been known that malware could remove CSP so it can inject its own scripts into the users browser in hopes of stealing information, as you can read about here.

The browser extension includes various features that will modify the default or custom settings of the browser including the home page, search settings and in some cases will modify Internet Explorer’s load time threshold, place a lock file within Firefox to prevent competing software from changing its settings as well as disable the browser’s Content Security Policy in order to allow for cross site scripting of the plugin.

source: https://malwaretips.com/blogs/trovigo-virus-removal/

Exactly how this malware removes the CSP is unknown to me, but I assume it's the HTTP-header that gets removed and not meta-tags. In either case you could use both and if a malware would remove the HTTP-header, the meta-tags would still protect the user (actually, it's the other way around; if you have CSP in meta-tags and via HTTP-header the meta-tags will be used).

Remember to use the same CSP as you have in your headers. Setting CSP via meta-tags is very easy; example: <meta http-equiv="Content-Security-Policy" content="default-src 'none' ; script-src 'self'"> in your <head> - departement.

There's even an extension that removes the Content-Security-Policy - header in the Chrome Store, you can install it here. Try it out and if you have CSP both in your headers and in meta-tags the extension will not work.

You can check if a website has CSP both via HTTP-header and in meta-tags with cURL:

curl -siL https://chloe.re | grep Content-Security-Policy
Content-Security-Policy: upgrade-insecure-requests; block-all-mixed-content; reflected-xss block; referrer origin 
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests; block-all-mixed-content; reflected-xss block; referrer origin"/>

Cache can provide security (?)

Can cache provide any form of security? Well, kinda. Cache is TOFU (Trust On First Use) so if the first connection is "clean" the next one can and should be as well.

However, the way browsers handle cache is according to me insecure. But Mozilla have developed something called "immutable" which actually can provide both security and a huge performance increase. The way immutable works is by instead of asking the server if the content has been changed the browser gives a local 200 (OK). Without immutable the browser asks the server, which gives a 304 (Not Modified) and this is sent over the network. The important thing to highlight here is the local 200 - this will not be sent over the network; the browser will directly give a 200.

Imagine that you are a malware and you have infected a machine. You can sniff and modify traffic on the computer because you proxy all the traffic. So what can you actually see and do? Let me show you.

Without immutable (F5 pressed) With immutable (F5 pressed)

Do you see the enormous difference? Because Javascript, stylesheets and images have the immutable - attribute on my blog, Firefox will instantly give a local 200 if they are cached. But Google Chrome receives 304 and the proxy sees this.

So back to being a malware. What does this mean for the malware? Well, if you can see the request, you can also modify it. If a cached object is immutable the malware will not see the network request and can therefore not modify it.

immutable only changes the F5 and refresh-buttons behavior - if you click on links you will always receive a 200 if the object is cached. immutable is currently only supported in Firefox 49 and forth, but hopefully Chrome and others will implement it.


Privacy is important and you should have a referrer-policy for your website if you haven't already. For those who don't know, a referrer is sent every time you navigate to a URL. As an example, click here.

A referrer-policy can be set via CSP but if we assume that a browser would remove the Content-Security-Policy - header, we can still use it in HTML-tags.

Example; for external links you could set referrerpolicy="origin" in <a href> - tags. (Worth to mention here is that best practice is always to use rel="noopener" on all outbound links. Read this.)

Did we win?

No. If we implement all of these things an attacker could just remove them. However, it would require the attacker to know about these implementations but as I stated earlier: I haven't seen any extension, malware or tool that actually look for these things (and I hope this article doesn't make it worse).

The best thing would be if browsers implemented so that some headers such as Content-Security-Policy aren't allowed to be removed or changed by extensions, but even then we wouldn't be safe because with a MITM-attack it would be possible to remove the header.

I do have some other tricks that you can use but these I have found to work on some existing extensions, malware and public knowledge.