Subresource Integrity
githubengineering.com
githubengineering.com
To be fair, running a mismatched version of the JS could already break things if the changes are big enough, but for minor updates, the user often won't notice the difference. Now, these cases are hard failures. That's not necessarily a bad thing, but I wonder if there's a path here to tell the browser "you have an old version of the content; go get the new version."
CDNs and invalidations can be tricky, and it sounds like this could lead to things being broken more often if you're caught in the window where one piece updates before the other.
Maybe I should refrain from posting my gut reactions (or at least wait until I'm awake first). =)
If indeed this is the case, subresource integrity needs a big warning sign about that. For me, your comment was that warning sign, so please keep posting while you're not awake yet.
1) Load the resource specified in src (from network or cache)
2) If there's an integrity attribute, verify its hash
Only if your page requires JavaScript to function and doesn't gracefully degrade. None of us would ever write that sort of page, would we?
For example the <noscript> tag works that way.
Hashes means you have to specify an exact version, so there's not an easy way to add integrity to things like Google's CDN for jQuery that has latest minor version update links for the major API versions of jQuery.
Of course, that means also adding a signature to the payload response (maybe an "Integrity: <hash>-<sig>" header?). So it's understandable why signatures weren't in scope for the first release.
If a hypothetical attack breaks TLS or you don't use it, you can just change the public key served.
Thanks for the downmods.
It would seem more desirable to be able to point to a specific version, instead of allowing a third party to be able to insert implicitly trusted code without acknowledgement.
I'd consider jQuery a third party. If only signatures are checked (and not content), then trusted third parties can push whatever code they'd like to your users.
If that's not convincing, consider the case where it's my own JS. I don't trust myself to run a CDN; I don't trust a CDN with the ability to modify my code. This allows me to build a single-page app that has ridiculously long cache lifetimes (so my own server load is low), and hand the actual, changing code off to a CDN, but verify my own signature on the data.
If that's not convincing, consider that data signing keys can generally be kept on non-internet-facing machines (and you can airgap, use a HSM, whatever), but performant SSL implementations by definition have to have their private key be in memory on an internet-facing server.
If you are already using a CDN, put your updated manifest (index.html) there as well.
Yes, I want that. I just want to control which third parties I trust. That's why it's called a "trusted third party", not just a "third party."
> If you are already using a CDN, put your updated manifest (index.html) there as well.
I'm not sure how this helps. Wouldn't this leave the index.html in the hands of the CDN, such that they are free to modify it?
I guess I just don't see why I would trust a library developer, but not a CDN. If you don't control the keys, you don't know who has them. (Although, I'd also argue that you don't even really know if you do control the keys)
>I'm not sure how this helps. Wouldn't this leave the index.html in the hands of the CDN, such that they are free to modify it?
I think you are right, as the system currently works index.html would not be safe. Currently you need a more dynamic system where the manifest is protected as well. A sidechannel (WebSockets, WebRTC) could be established to securely deliver updated manifests (which a lightweight client would translate into DOM operations).
I'm not capable of running a CDN myself. So I have to trust someone. I might as well minimize the number of potential someones I trust; I claim that gives me a concrete benefit.
Since I'm not writing jQuery myself, I'm not minimizing it myself, and I'm certainly not minimizing it by hand, I do already have some trust in the jQuery project and their infrastructure. I don't currently have any trust in a CDN. If I'm going to move to using a CDN, I'd like a route with lets me put slightly more trust in the jQuery project (who I already trust to some extent) than in some completely new party.
Alternatively, I don't have to trust jQuery. I can trust someone else who's good at running secure build infrastructure, auditing libraries like jQuery or anything else, and minimizing and signing the result. (This is, loosely, analogous to the role that a Linux distribution plays.) Then I can choose to trust these people or not based solely on how good they are at security, choose my library authors based solely on how good they are at writing libraries, and choose my CDN based solely at how good they are at distributing content. I don't have to conflate the security trust with anyone other than the people I intentionally choose to put security trust in.
I don't think one approach is necessarily worse, but one can provide all of the essential functions of the other, without allowing unseen code to be pushed to users.
Is there any security gain from doing that?
With a signature, you could specify "include cdn.com/jquery-X if signed by the JQuery project", so JQuery could publish security updates and those could be rolled out to the CDNs and included in all pages automatically, without the siteowners having to make changes (if the security fix doesn't break compatibility).
For your own content, you'd mostly gain the convenience of not having to update the hashes on all the pages including the resource.
TL;DR:
* hash: need to compromise the main website, that supplies (and authenticates) the hash
* signature by CDN: attacker can either compromise the main website OR <del>the third party CDN</del> <ins>author/signer of the third-party resource</ins>
(edit: correction as pointed out by response)
Of course it's a trade off. For stuff like Google Fonts, the Facebook like button etc I'd expect that hashes won't become common, because the effort of publishing changed hashes and embedding them into sites is to big.
(edit: e.g. see https://en.greatfire.org/blog/2015/sep/popular-chinese-ios-a... just out today)
Especially in the case of a library developer, they hold the keys to many websites, so there is extra incentive for an attacker to break that rather than "some random guy's website". The more third-party signers you trust, the more holes you (and your users) have.
Furthermore, you are forcing your users (who actually run this code) to place their trust in these parties too, which is not a great thing (transitive trust) to force upon someone. (This is not the case for e.g. depending on system libraries explicitly installed by the user.)
This matters: If someone wants to hack my company, they're not going to do it by hacking Github's CDN. They're going to do it by targeting particular employees -- probably focusing on those who have the least security experience. To reduce risk, I need to give each team member the least authority they need to do their job. Github is making it really hard for me to do that; I tend to have to give "admin" rights to everyone. :(
You're just trading one problem (someone learning I previously requested a file) for another (leaking referrers to a CDN).
Also, if you're loading "highly sensitive/top secret" data with a <link integrity="" href=""> or <script integrity="" src=""> tag, you have bigger problems.
Obviously, this technique will only work if the contents of that resource are constrained enough that it's possible to guess them with brute force. Depending on how SRI interacts with the browser cache, though, it may be possible to make guesses very quickly -- it is likely that the browser will only fire one HTTP request for the initial attempt, and will load the resource from cache for all subsequent attempts.
Frankly I'm relieved to see that browser vendors and leading tech firms are maintaining control of the situation and protecting users, even if driven by self-interest.
Widespread adoption of Subresource Integrity could
have largely prevented the Great Cannon attack
earlier this year.
Sorry, it wouldn't have. From the CitizenLab report [1] on the Great Cannon attacks: In the attack on GitHub and GreatFire.org, the GC
intercepted traffic sent to Baidu infrastructure
servers that host commonly used analytics, social,
or advertising scripts. If the GC saw a request
for certain Javascript files on one of these servers,
it appeared to probabilistically take one of two
actions: it either passed the request onto Baidu’s
servers unmolested (roughly 98.25% of the time),
or it dropped the request before it reached Baidu
and instead sent a malicious script back to the
requesting user (roughly 1.75% of the time). In
this case, the requesting user is an individual
outside China browsing a website making use of a
Baidu infrastructure server (e.g., a website with
ads served by Baidu’s ad network). The malicious
script enlisted the requesting user as an unwitting
participant in the DDoS attack against GreatFire.org
and GitHub.
So the idea is someone runs a site with: <script src="http://baidu.com/ads.js">
When visitors request these scripts the request passes through the "Great Cannon" which 1.75% of the time serves a different script instead. That malicious script makes lots of requests to the victim sites, and they're overloaded.To prevent this sort of attack with SRI you would need to change your page to look like:
<script src="http://baidu.com/ads.js"
integrity="hash of the real ads.js">
The problem is, Baidu isn't going to be willing to commit to always serving the same ads js: they need to be able to make upgrades.SRI is useful in the case where the entity producing the html is referencing js that they've uploaded to a third party CDN or js where they choose what version to run, but not in the normal "include a snippet and we'll do stuff to your page" model.
(To block the Great Cannon there, what would have worked would be moving the js serving to HTTPS.)
The way that this fixes the issue is by ensuring that the file being loaded on those thousands of websites is the correct one, and not the malicious attack script that was injected by the Chinese government or other such actors, otherwise it's not run at all.
Could the Chinese government rewrite the HTML of all these thousands of websites to also change the hash? Theoretically yes, but practically it makes it much more difficult.
It also lets you use CloudFront as a CDN for your own JS without having to trust them to serve the content as you described it, if you calculate your hashes based on the scripts you sent them.
If you start seeing unexplained errors on pay-as-you-go phones, you'll know why; although if this facility gains popularity then I'm sure they'll be pressured to stop modifying content.
IPFS comes to mind.
original: IIRC CSP already has hashes for resources, which also would handle this purpose.
As a side note, there's at least one CDN already hosting fake copy of bootstrap - I've seen a mlicious extension loading it in my report-uri.io logs.
Bravo. :)
Subresource integrity is in some ways more important than "HTTPS Everywhere", because the MITM-as-a-service sites such as Cloudflare subvert HTTPS Everywhere. For security reasons, you might choose to serve your home page and a few security-critical pages from your own server, without using a CDN. But run everything else through the CDN, using subresource integrity to keep the CDN honest.
With subresource integrity, many items no longer need to be encrypted. This is good for security. Encryption interferes with caching, and HTTPS in front of caches means that the attack surface is larger, and includes the CDN.
(Yes, there's an argument that HTTPS conceals what the user was browsing. Not really. Checking document length will provide a good hint on what static asset was read. The pattern of document lengths requested tends to fingerprint the page being read.)