Heartbleed and JavaScript Crypto

Reading the news on Heartbleed in the mainstream media these days got me thinking. It's hard enough to grasp the consequences as a technical person, but trying to explain to my girlfriend why she might want to change the passwords to all of her favorite sites made me think if things couldn't be different.

There is no such thing as bug-free software and anyone writing a complex enough application will experience critical security bugs sooner or later. For most web applications the single line of defense to protect user's data in transit has pretty much been SSL. Much has been said about JavaScript cryptography here and here. While these were great blogposts in their time, in the wake of Heartbleed I would like to discuss how JavaScript crypto can  add a valid layer of security if it is deployed correctly.

Threat modeling

It's very trendy to criticize JS crypto. And while some arguments are valid, more often than not they are made in a  vacuum and without context. When discussing security mechanisms we should discuss what we're trying to protect and from whom. For web apps I see two main categories:

Services you trust with your data

I will use an example here. Elster is a system used by the German government which lets citizens do their taxes online. You can visit it at https://www.elsteronline.de. What most people don't know is that Elster uses JS crypto to authenticate users with a passphrase protected Elster certificate which is decrypted in the browser. Before the crypto trolls start getting hungry, let me explain why this makes perfect sense.

  1. You trust Elster with your tax data and therefor it's ok to trust their web server for authentication. This is not a zero knowledge type application where you're trying to protect your data from a service provider. The user does not want to protect his data from Elster. It is actually Elster that wants to verify that you are the person with the corresponding tax identifier.

  2. The alternative is a Java Applet. Yes, this is how the authentication worked before they switched to the Java-free version. And we all know Java is not famous for it's security track record. So given the choice I'd take JS crypto over having to activate Java in my browser any day.

What I'm trying to say with this example is that there are use cases where deploying JavaScript cryptography over a standard web server setup can make perfect sense if you trust the web server with your data. Another example like this is Netflix, which uses WebCrypto and new DRM standards to protect their premium content. But basically any website that sends the user's password in plaintext to the web server could do hashing client side. This would prevent my girlfriend from having to change all of her passwords when something like Heartbleed happens, since most web servers don't use forward secrecy and the compromise of private SSL keys can be used to decrypt stored traffic after the fact.

Services you don't trust (completely) with your data

Now that we've established an example for where a normal web server deployment can make sense, lets look at an example of where it does not make sense. Whiteout Mail is an application designed to make PGP encryption easier for average users (I'm a co-founder and developer). When we started designing the app, we thought about deployment via standard web hosting due to ease of use, but that simply does not make sense for this type of app.

Crypto.cat got a lot of heat by security experts back in the day when it was  served via a web server. You can read the two blogposts I linked at the top or just take Bruce Schneier's word for it. This is why crypto.cat changed to an App/Extension based deployment model. This is has several reasons.

  1. You don't trust the web server with your data. If you did you wouldn't be using client side crypto. Every time you click on a link, you're basically "installing" the client side code on your machine to be executed in your browser. The term "drive by web" is usually used here, which basically means that browsers are designed to run arbitrary untrusted code. For zero knowledge type apps, this is not the model we're looking for.

  2. We want auditable static versions. For privacy sensitive applications it is a best practice for software to be installed locally with a static version. This version is signed, downloaded, verified locally by runtimes and can be audited by independent security researchers.

Best practices for JS crypto

It is a bit simplistic to split the world into these two categories, but for starters I hope it will help developers working with the new Web Crypto Apis to understand where to put their application. To help you get started here is a summary of learnings from our work on Whiteout Mail.

  1. For zero-knowledge type applications, use packaged apps. They are available for Firefox, Chrome, and for mobile there's PhoneGap and Google's Chrome mobile apps. Even Microsoft now supports first class HTML5 apps with universal apps for Windows 8.1. The advantage besides security is also that your app can be found in the platform's native app store, which is where most users look to find apps anyway.

  2. For web server deployments use SSL with forward secrecy and activate HSTS (HTTP Strict Transport Security) on your web server. Chrome even supports certificate pinning now.

  3. You should use CSP (Content Security Policy) to protect users against XSS. It's very easy to configure and there is no excuse to not use it. This also forces you to write code with a clean separation of concerns.

  4. Parse data from a server with JSON.parse (never with eval) and escape all strings before inserting them into the DOM. If you're using a framework like Angular.js it will escape models automatically before rendering and it's even compatible with CSP!

  5. Always code in ES5 strict mode. You can use jshint inside your favorite editor (I use sublime) and tools like grunt make it easy to integrate into your test suite.

  6. Write unit tests rigorously. I cannot overstate how important this is. You will not catch regressions otherwise.

  7. Do code reviews before merging changes to the master branch. This will not only catch some security bugs, but also increase code quality and maintainability.

  8. Do regular security audits by independent professionals. They will find stuff. Trust me.

  9. If you can, open source your code. This isn't possible for all business models, but if you can't open source the entire client app, at least open source the crypto bits to allow inspection.

Potential benefits of JS crypto over native crypto

I know I'm stretching a bit here, but bear with me for a moment. Here are a few aspects in which I think JS crypto has an advantage over native crypto:

  1. Installed JS crypto apps source can be viewed. There is a great blogpost by Jon Callas from Silent Circle about the verifiability of native apps. You should definitely check it out. He explains why open sourcing Silent Circle would not solve the problem Ken Thomson first articulated in his Reflections on Trusting Trust. For interpreted languages such as JavaScript, we don't have this problem as the app is deployed as source code. You still need to trust the browser runtime (which is of course compiled code), but it minimizes the amount of code-bases you need to trust to your OS and your Browser.

  2. JavaScript is memory safe. It seems obvious but often overlooked. The bug that caused Heartbleed was due to a problem that C compilers leave to programmers: memory safety. Although JavaScript is slower in performance due to this property, buffer overflows like the one that caused Heartbleed are a lot less likely to happen in JavaScript.

  3. One codebase means less room for error. I explained this in an FAQ for safewith.me (a hobby project before whiteout.io). The lines of code in any project correlate to the number of bugs sleeping in a given software. If there's anything we've learned from Heartbleed, it's that no matter the choice of programming language, the biggest risk is human error. Since we're serving all platforms with a single codebase with JavaScript, were reducing the room for human error and therefor the attack surface. It also makes the code easier to understand and audit by outsiders.