Javascript Cryptography Considered Harmful
matasano.com
matasano.com
One of the things my company does is security testing of web applications. Regularly we encounter 'creative' use of cryptographic techniques (both in the browser and server-side) and each time it makes the hacker in us smile, because we know it is not a matter of 'if' we'll crack it but 'when' we'll crack it. Good crypto is a roadblock, bad crypto is just a challenge. And although it is very hard to decide if the crypto is 'good (enough)', the 'bad' is usually glaringly obvious.
With the current state of crypto in the browser - just forget it. That's what Thomas is trying to get across: forget it - if you think you've found some smart way around one of the weakness he addresses, you're very most likely wrong. And even if you seem to have got it right, you're probably wrong still without anyone realizing it (yet).
Same is true for building a crypto-system from primitives. Use what's out there, designed by the few people who know what they're doing.
Remember: from the defensive side you need to get everything right. As an attacker I only need 1 hole. That's what makes it "capital-H Hard".
To say that localStorage is literally no better than server-side storage is a strong statement, and one that does not appear to be literally true. Taking issue with that equivalence is not the same as saying that any particular system/design is safe as a whole.
And I think that localStorage is indeed literally no better than server-side storage. Whether it is any worse depends on the situation - but better it is not.
"This ignores the scenario of app deployment models like Chrome Packaged Apps, in which the JavaScript code gets downloaded up-front and then is only used locally. Since you don't re-download the code every time, you only depend on the security of the code once, up-front, instead of on a continuous basis. You aren't affected by server compromise (well, no more than compromise of your OS vendor, but surely you aren't arguing that we might as well send all our keys to Microsoft, Apple, and Canonical)."
It's true that you do not re-download the code every time. Still, the trustworthiness of the code you received initially depends on the trustworthiness of the server you received it from.
So you say, "but what if I have reason to trust it initially and not later on?", e.g. when the government comes knocking.
Well, there are two things to keep in mind in that case:
a) you download other stuff with your browser. Stuff that can influence the environment where your secure and trusted packaged app runs in. See also comment of zimbatm - even if it is not formally meant to be that way, in practice there are bound to be ways around any limitations - sandboxing in browsers is still nowhere near perfect unfortunately. Server security is not entirely peachy either, but at least on a properly secured server only a limited, carefully screened set of applications is allowed to run which makes things hell of a lot easier.
b) Chrome packaged apps support auto-updating, so unless you take steps to prevent that from happening, you're never sure you're running the same version today as you did yesterday. Again, trusting the server to serve you a trustworthy version of the app repeatedly. And if you're trusting the server already, local storage is no better than server side storage.
So I guess you could say local storage is better than server side storage (for some definition of better) if you run one and only one packaged application ever in a specific installed browser in an isolated environment. The browser + locally installed web app then effectively becomes a natively installed application without an Internet dependency. More secure indeed, but kind of defeating the purpose of that whole web thing ;-)
Again, I think this reduces to "Microsoft/Apple can auto-update your OS, so you might as well send your keys to Microsoft/Apple." Would you argue that? If not, why?
When attempting to create crypto code using an interpreter or a byte code virtual machine, additional side channels are created by the differences in the execution compared to executing native code. Crypto code should be written in Assembler or C code where the assembly output is reviewed by the author. This is the only way to create code that does introduce side channel information that can be used with timing attacks, cache attacks, branch predictor attacks, etc. This introduces a problem because it takes a cryptographer and a hardware architecture expert in the team to write safe code for cryptographic primitives.
This does not mean you can't safely use crypto from interpreted languages, as long as the cryptographic primitives are good native code.
Of course, changing the interpreter seems to be an order (or two) of magnitude easier than supplying malicious interpreter. However, I would argue that if you are able to replace JavaScript engine you could do same with whole browser, so SSL is also worthless...
when a virtual machine is introduced the just in time compilation itself may introduce these dependencies between timing/voltage and the key used in decryption even if the source code does not appear to. however, unlike in c, you can't just go examine the compiled assembly to ensure a timing attack wasn't added by the compiler. the actual native code becomes a function of the virtual machine installed.
the guy above you knows what he's talking about.
The same is true for JS crypto - yes, it is not as safe as crypto in native code, but it can be used to add an additional layer of security in certain (non-critical) use cases.
[Disclosure: I run AES.io]
you are saying not safe as if the term has a standard meaning across all contexts. anything can be cracked - the question is whether the time it takes to crack a computer is worth it compared to the data stored on the computer. in almost any case you actually need cryptography and it's not just a nice to have (aka credit cards, personal information) it's not worth using anything but native code.
That point of yours actually points to a potential vulnerability in server-side (possibly native-code) encryption, not client-side encryption, which we discuss here.
I completely agree with you that anything can be cracked, and JS crypto more so than _some_ native-code cryptosystems. My point is that using JS crypto for some non-critical applications (e.g. as an alternative to corporate IM/email) can be useful and convenient.
...it also relates to the time it takes to generate a response from the client, server was just a specific example. side channel attacks are extremely flexible. the only reasonably secure crypto code is code that executes in the same amount of time no matter the execution path.
lastly, if you're using protocols to prevent mitm attacks, you've already reached a level of sophistication where you may as well just throw javascript crypto out and use a real crypto package. if you're not using those protocols the information is basically free and you shouldn't add any crypto because that might mistakenly convince people that they're secure.
remote timing attacks contain noise by default, as they rely on server communications passed across a network with latency instead of examining the hardware directly to determine execution time. if you compare the network latency to the cache timing you'll find the noise is actually pretty fucking substantial.
a timing attack relies on a average difference in execution time between two paths of code. certain noise isn't going to protect you - for example, if everything on average takes 200ms longer, the average difference in execution time is still there.
timing attacks are only easier against native code written by people who don't know what they're doing, which means they made different execution paths take variable amounts of time, didn't examine the generated assembly by a hardware expert, and didn't bother to mask the crypto operations with proper noise generated using a cryptographically secure prng.
"That scheme is, at best, only as secure as the server that fed you the code you used to secure the key. You might as well just store the key on that server and ask for it later. For that matter, store your documents there, and keep the moving parts out of the browser."
This ignores the scenario of app deployment models like Chrome Packaged Apps, in which the JavaScript code gets downloaded up-front and then is only used locally. Since you don't re-download the code every time, you only depend on the security of the code once, up-front, instead of on a continuous basis. You aren't affected by server compromise (well, no more than compromise of your OS vendor, but surely you aren't arguing that we might as well send all our keys to Microsoft, Apple, and Canonical).
Also I feel that this analysis conflates security with access. You may trust a company to keep their servers secure from compromise, but want them not to have access to the documents when the government comes knocking.
For example, maybe I want to upload a file to a server, and I trust them not to try to steal my data, but I don't trust my government not to confiscate their servers. In that case, SSL + browser cryptography is adequate to give me the assurances I need that the government won't be able to get access to my data, even if the service's engineers could.
> And if you have SSL, why do you need Javascript crypto? Just use the SSL.
Maybe because SSL only solves one problem, and other cryptographic algorithms/systems solve other problems?
This makes security measures like JavaScript crypto which only protect against passive eavesdropping quite a bit more useful than you might naively expect.
They pushed modified client software to some users after a Canadian court issued a court order.
(http://www.wired.com/threatlevel/2007/11/encrypted-e-mai/)
(http://arstechnica.com/security/2007/11/secure-hushmail-can-...)
Getting a court order doesn't seem particularly hard; it's certainly not the full might of the NSA applied to cracking the crypto.
If the government might have the ability to confiscate their servers they also have the ability to compromise their service during use. So, if you don't trust your government you can't trust their servers either, regardless of whether you trust the service's engineers or not.
This actually matters for sites like Mega. There have been previous examples of file storage services falsely promising that all your files would be encrypted and their staff would be unable to access them, for example Dropbox. If all services use server-side crypto there is no way to tell the difference between a service that lies about this and one that's honest. With Mega it's possible - not easy, but possible - to check exactly what kind of encryption is being applied and where it's getting the keys from. You have to go through every bit of content manually with a fine-toothed comb to be sure, of course, and there are various ways to obfuscate things, but it's still better than not being able to see the code at all.
You can verify the JS, CSS and HTML only for a specific request and only if you have sufficient knowledge, meaning you have studied cryptography and even then it will take a lot of time because it is not possible to not verify that code manually.
This offers no additional security at all for anyone but the most paranoid cryptography experts.
This is not much different from how everything else works. Hopefully the expert criptographers release tools to help the uninformed masses use stuff or at least break the news is some bad shit starts going on.
I agree you are right. Once you CAN deliver the code securely (like the author admits is possible) through SSL you should be able to encrypt things in the browser and send then to the server encrypted. This is what the author is saying as well.
I have been raising alarm about this for a long time. Automated updates are dangerous, how many users make it absolutely certain that every update is secure? Well, I can tell you nobody ever does. Because secure updates or software doesn't exist at all. Even if the previous version was secure, the next version could be boroken by mistake or on purpose, or you could just get espionage version delivered which is made just for you.
Unfortunately there are countless programs that do not make update delivery in very secure manner at all. Plain http, no signatures etc. That's quite much 100% fail.
Ecmascript 5, the latest version of the Javascript standard, provides the ability to lock down the malleable runtime. Functions can be frozen so that no later code can overwrite or change their behavior. For more information, see this talk[1] by Mario Heiderich in 2011 or his slides[2].
[1] https://www.youtube.com/watch?v=yuNfO6I6pEA
[2] https://www.owasp.org/images/a/a3/Mario_Heiderich_OWASP_Swed...
Of course it may be possible that one day there is a way around that issue, but currently there is not. Not even academically let alone practically. Hence Thomas's next remarks about the impossibility of 'graceful degradation' for crypto-in-the-browser issues.
On another side, if you assume that other vulnerabilities don't exist, and you do such things online like banking or trading, and you accept that the sites use JavaScript, I don't see any argument why the crypto primitives which run in addition to the rest of the code, everything delivered over TLS and from the same site, are any more suspicious than the rest of the JavaScript.
The advantage of the encryption on the client side is obvious. Of course, it would be even better to have the client side encryption controlled by the user separately from the site. But under assumption that I personally control the server from which I deliver my html and JavaScript over TLS, I still feel better having the possibility to encrypt something that I'll upload to the server as long as I assume that the browser is not attacked.
The only thing missing is the possibility to somehow checksum the delivered html and code and then "lock" that in my browser. It's not something scalable, I know.
But the problem is never that much technical as it's "political." Consider Dropbox: in many use cases, they would be able to have all the encryption on the client and not to deliver the key to them. However they do deliver the key "because the users will need it." Who says that? They, and I can't choose.
Technically, the solution can be certainly achieved, the problem is that it's not an interest of the current service providers.
Maybe is Mega the first one that really has such interest?
Every attack against an otherwise secure system requires these two steps:
1. Compromise a computer or environment by gaining physical access and changing something about the computer/environment (e.g. creating a false passphrase screen, or installing a hidden camera).
2. Trick the user into giving up their passphrase/private key after step (1) is complete.
Once a machine or environment is compromised, it's just a matter of time before (2) happens and your security is broken. Physical access to a machine or environment where a passphrase is entered is required because network security is (effectively) perfect with modern crypto.
If you are truly paranoid, you'd have to constantly secure your environment and physical hardware, and whenever either went out of your physical control, not use your passphrase/private key. Full stop.
I totally agree with you about Backblaze; at least with Tarsnap, the physical machines and environments that are actually part of the cryptosystem are in your own control.
Oh, and the "in fairness" was that compromised clients have the same problem as compromised servers. Perhaps I could have worded it better – I don't have any problem with what you wrote at all.
I'm imagining a system (kind of like tar snap) that backs up my files all pgp encrypted ith my public key, and which allows me do download those encrypted files (which I can then decrypt locally).
If the pgp encryption is done client side (by a native app, not in-browser), and the "backup service" oly ever sees pgp encrypted files - is there some other hole I've not seen there?
(I guess theres metadata leakage with that scheme, the number and sizes of backed up files could be determined, even if the contents are secure)
Doesn't that mean the browser can't access the data? The browser can access an encrypted lump of stuff, but it doesn't encrypt it or decrypt it.
The "if SSL, why JS crypto?," DOM, and "chicken-v-egg" trust problems seem more like straw-men and sophistry though. Desktop crypto underwent an iterative evolution with early adopters bearing the bulk of the risk too. (Mega got the digest part wrong, but they fixed it, for example.) SSH doesn't use certificates, but you can read the host fingerprint and follow the chain of trust that way. If people are going to use crypto, they have to take responsibility for these pieces, which is improbable en masse. "[T]he security value of a crypto measure that fails can easily fall below zero" definitely rings true. Repeated malware infections, however, suggest peope don't even learn after they are burned... "Normal users" can't be bothered to update their browser or verify trust (leading to VeriSign having complete power, for example), for the same reason "normal" people don't use the existing native encryption (GPG/PGP) and, if they did, there would be no need for JS crypto.
Not a lot has changed since then, sadly.
EDIT: There are some promising comments in their archives, "A high-level API is also perceived to be the only kind of API that is usable by the majority of web developers. I agree we should not put this off to long." [1]
http://lists.w3.org/Archives/Public/public-webcrypto/2012Sep...
Anyway, that's how I'd do it. But yes, doing crypto within a page seems to be asking for trouble.
I don't quite get why
> You could use SSL/TLS to solve this problem, but that's expensive and complicated
You can get an SSL trusted certificate for a few bucks, and installing SSL is probably one of the most well-documented sysadmin procedures on the web. How can it be more expensive or more complicated than implementing your own javascript crypto?Obviously that's a bit impractical for most websites, but it could make sense for a site whose primary raison d'être is encryption.
Also way out of my league, btw :-)
I don't know if this situation would be common but I have an idea of where it could work. Perhaps a web app you completely trust could talk to an API you don't trust over CORS. The web app you completely trust would only talk to the api you don't trust over XHR and wouldn't eval anything it got back.
(http://users.guardian.co.uk/signin/0,12930,-1,00.html)
This website claims it's their script: (http://pajhome.org.uk/crypt/md5/)
I repeat: security is not abstract, it depends on the problem and the trade-offs.
If this hurts please check your mind.
And feel free to downvote OF COURSE.
Be happy.
Check back in 10 years when the majority of people aren't running browsers from 2008.
Edit: The oldest version on archive.org is Sep 2011, so it's at least 16 months old.
http://web.archive.org/web/20110815000000*/http://matasano.c...
Security is a set of layers and trade-offs.
Harmful for what? That is the question.
Your computer is considered harmful. Did you check the hashes of all the software you downloaded? Oh wait there are no checks to be done for the little app you got a couple of months ago...
So: take an enemy and look if what you do is reasonable enough.
Flying is considered harmful, hence the TSA.
Remember the github fiasco? But you still trust them do you? I might (I do not) consider github harmful as well.
var x = new Uint8Array(10);
window.crypto.getRandomValues(x);
console.log(x);
It's also supposed to work in FF, but for some reason doesn't:you can swap the toString method too.
And it was not my intention to 'bash' anyone - if it came across as such I apologize.
If that native C application runs on a server that is fully under your control you stand a far, far better chance then when that native C application (say a web browser) runs on some computer not under your control. Especially if that native C application is explicitly written to accept run-time addition of random third-party code (aka browser extensions).
document.body.innerHTML += "<iframe></iframe>";
document.body.childNodes[document.body.childNodes.length-1].contentWindow.crypto.getRandomValues;
And please don't talk about how the JS engione of the browser can be compromised too; I know that but here we are aiming for practical applications not a philosophical debate about how everything is just an illusion.Also I changed "document.all" for "document.body.childNodes" that is cross-browser and can't be compromised.
Still, I think that "but here we are aiming for practical applications not a philosophical debate about how everything is just an illusion." is a dangerous statement. Some people would say the same about something like sql injection, or cross-site scripting (Really? Yes, really, I encounter them on a regular basis).
With security issues the border between 'practical' and 'not practical/philosophical' depends on your threat model. If the kind of adversary that is able to compromise your JS engine does not appear in your threat model you can ignore the possibility of your JS engine being compromised and your solution may be good enough. If however that kind of adversary does appear in your threat model you do not have that luxury and your solution is not good enough.
That's not philosophical, that's real world practical.
document.body.innerHTML += '<script>document.write("<plaintext>"); document.body.childNodes[document.body.childNodes.length-1].contentWindow = {crypto: {getRandomValues: function() { alert("Ha!"); }}};</script>';