Wednesday, June 18, 2014

Javascript Crypto Is Useful

Update: I rewrote this article, after taking an English writing class on Coursera. I only removed cutters, and kept the content intact.

Update: Nate Lawson wrote a thoughtful comment..



Update: A few more thoughts from Brad Hill of PayPal and W3C.


Update: why it's hard to maintain large code bases written in Javascript or other dynamic languages.


One year ago I wrote my first Javascript crypto library that is a thin wrapper of End-To-End. Since then I've designed and written many such wrappers - eight of which are deployed in popular Google products and services. Javascript crypto can help solve many problems, some of which I'm going to discuss in this post.


Some people have strong feelings about Javascript crypto, and with good reason. Writing crypto code in Javascript is difficult, because of the lack of types and the permissive nature of the run-times. You can always shoot yourself in the foot if you don't know what you're doing in any languages, but you don't even hear the shot in Javascript: the run-times don't usually complain, but they just silently give you an incorrect result.


For example, if you have an array x of 10 elements, accessing x[10] or x[11] won't throw an out of bound exception, but return undefined. This behavior is hostile to crypto code, as demonstrated in this neat exploit discovered by Bleichenbacher, the grandfather of many crypto attacks:
* Visit http://people.eku.edu/styere/Encrypt/JS-AES.html. This is a Javascript implementation of AES.
* Encrypt this string: ưưưưưưưưưưưưưưưư as ASCII.
* Decrypt the ciphertext.
* XOR each character of the decryption with 0x52.
* And the result is the key.


What happened? The program expects ASCII inputs, but we give it Unicode. The function that causes the vulnerability is SubBytes


// do S-Box substitution
function SubBytes(state, Sbox)
{
  var i;


  for( i=0; i<16; i++ )
     state[i] = Sbox[ state[i] ];


  return state;
}


The state variable holds the input. If state[i] >= 256, state[i] would become undefined,because Sbox is an array of only 256 elements.


Many libraries have this “type confusion” vulnerability. How do you fix it? First, you need to validate input. If a function accepts a byte array, it should always check input is an array whose elements are bytes. Secondly, you need to minimize type conversions. Instead of accepting strings in one function and big integers or byte arrays in others, we need to use a single type everywhere. Thirdly, instead of plain old array of numbers you should use typed arrays. When a function accepts an Uint8Array it knows that each element of input is a byte, without doing expensive type checks. Finally, you should Closure and annotate variables and parameters with its pseudo types. The Closure compiler will type-check your program and report type mismatch bugs.


After fixing the type problem, Javascript could only be as bad as other languages when it comes to XOR things together. I've seen broken crypto in C, C++, C#, Java, Ruby, Python, or ActionScript, etc. I disbelieve that Javascript crypto is too bad to do anything with it. The language has its annoying flaws, i.e., numbers are stored as floating point in a 52-bit mantissa, broken bitwise shift left, etc., but they only make the task of programming a crypto library a bit more fun and challenging, not riskier.


Most people consider Javascript crypto harmful after reading this article by Matasano Security (disclaimer: I worked at Matasano before joining Google, and I've always been a huge fan of @tqbf, one of the company's founders.) If you haven't go read it, I'll wait. The article has a good point, but many other criticisms are outdated. Javascript now has a secure PRNG. WebCrypto provides many native crypto primitives, including a secure key storage. Guess what do Stanford, Google, and Microsoft have in common? Each has released an open source Javascript crypto library. No other languages have enjoyed this tremendous support.


That said, I totally agree with its main criticism: if you distrust the network or the server, doing Javascript crypto, with the code being loaded directly over that network from that server, makes you vulnerable to active network attackers or the server itself. If the server or the network is considered as adversary in your threat model, don't trust Javascript crypto code delivered from them. This threat model doesn't, however, apply to most applications. For example, as a Chrome extension End-To-End is immune against this threat. Next I’m going to showcase a few applications of Javascript crypto that are safe too.


1. Build crypto clients
Without Javascript crypto you don't have SSH-In-A-Tab, PwdHash, various Bitcoin wallets, etc. Do you have a Chromecast? If you don't, go buy one. It's amazing. Did you know that it uses Javascript crypto to make the out of box setup so easy and yet secure?
We're living in a world dominated by apps, it's easy to forget that instead of installing an app to do something you can visit a website on your favorite browser and get the same thing done instantly, thanks to the power of Javascript.


2. Stay out of scope of PCI DSS


Large scale systems usually process web requests as follows: Browsers <----> Load Balancers <---> Reverse Proxies <---> Frontends <---> Backends. If the requests contain credit card numbers, all intermediate components must comply to the PCI DSS standard, which could be annoying. To keep the load balancers, the reverse proxies or the frontends out of scope by you can use Javascript crypto to encrypt credit card numbers on browsers and only decrypting them on backends.


3. Avoid data leaks


Because many advertisers are slow in upgrading to HTTPS many advertisement serving systems still use clear text HTTP. Despite lack of encryption, these systems collect and transfer tons of personal data. Javascript crypto, served from a HTTPS origin, ensures collected data in motion are safe against interception.


4. Reduce latency


You have a big fat website that takes many seconds to load because it has megabytes of Javascript code. You may want to cache the base code in localStorage, and download only small diffs. This design is, however, vulnerable to cache poisoning attack. If an attacker manages to insert a backdoor to the cached code (by exploiting a XSS, for example) they will have permanent access (see this talk by my colleague Artur Janc for more details). You need to protect the integrity of the cached code by using a digital signature. You sign the code on the server, and use Javascript crypto to verify the signature.


Conclusion: Programming crypto in Javascript is hard, but doable. As usual if you don't know crypto, you should use good libraries developed by professionals. Javascript crypto has many applications, and has received unprecedented support from Stanford, Google, Microsoft and W3C.

4 comments:

Minh Hoàng said...

Chắc phải cải thiện khả năng đọc hiểu tiếng Anh của mình quá -_-!

Ankur said...


Nice,
Thanks for your greatful informations, working in, ASIAN AFFAIRS MAGAZINE.
URDUTAHZEEB.
Try to post best informations like this always

Thai Politics: Is Thailand out of Yingluck as instability looms?


Unknown said...

I hope more stable JS Crypto libraries are released. There's a great need of them for internet users.

It's the only way to trust that your data is actually being encrypted, instead of blindly believe what the company in question wants to sell you.

I'm not near to being literate about these topics, but wouldn't a browser extension checking if the crypto code changes from a trusted initial version of it and/or checking its hash against a server stored hash be enough?

Thanks for your efforts in making JS crypto possible :)

Unknown said...

Enough to check its integrity, I mean.