AjaxPatterns
This is the original book draft version for the Host-Proof Hosting Ajax pattern, offered for historical purposes and as an anti-spam measure.
You're probably better off visiting the Live Wiki Version for Host-Proof Hosting instead, where you can make changes and see what others have written.

Host-Proof Hosting

ASP, DataCloud, Key, Secure, Untrusted

Goal Story

Reta is dismayed to learn that a malicious hacker managed to download a chunk of the company's database, containing personal details of all the customers in her store. Fortunately, she also learns that the pass-phrase she always entered was actually used to encrypt the all that data, so the hacker won't be able to make sense of any of the contents.

Problem

How can you mitigate the effects of unauthorised access to your application data?

Forces

  • Web apps require some form of persistent data, to hold information about users, business state, past events, and so on.

  • Security restrictions prohibit storage on user's own hard drives with standard web technologies. Even with “Richer Plugin”s that allow it, many benefits of using a web app in the first place are lost. This means that persistent data is usually stored server-side.

  • Cookies allow a some data storage within the browser, but cookie data is also transmitted to the server, where it's vulnerable.

  • Server-side storage is open to abuse: the administrators, along with anyone who is able to gain access to it, are able to extract sensitive information by reading the data, as well as effect malicious changes by tampering with it. The abuse can occur within an organisation's own IT department or a third-party hosting company entrusted with the data.

Solution

Host sensitive data in encrypted form, so that clients can only access and manipulate it by providing a pass-phrase which is never transmitted to the server. The server is limited to persisting and retrieving whatever encrypted data the browser sends it, and never actually sees the sensitive data in its plain form. All encryption and decryption takes place inside the browser itself.

Just what does secure hosting have do with Ajax? The Ajaxian twist comes in the maintenance of the pass-phrase. You could use the browser-side encryption with a conventional application, but the pass-phrase would have to be entered upon each page refresh, since no Javascript state survives a reload. With page refreshes occurring every few seconds, it's completely unusable. However, using Ajax to avoid page refresh means you can retain all session state in the browser, so the pass-phrase need only be entered at the start. After being entered, it can be retained as a standard Javascript string and will disappear from the browser when the user quits the browser or visits another site. Suddenly, Host-Proof Hosting becomes usable.

Incidentally, don't take this pattern to be a new "Ajax-HTTPS" protocol - the idea here is about how the data's actually stored, not how it's transmitted. In theory, the data itself need not travel over a secure connection, because it's already encrypted. In practice, a secure connection might be worthwhile in order to reduce some of the vulnerabilities described below.

Before you rush off to upload all your trade secrets to Shonky Hosting Inc., you should be aware this idea isn't foolproof. On the one hand, the host is assumed inherently untrustworthy. But on the other hand, the script for the browser application is held right there on the server, and the browser runs whatever scripts come down from that URL. This leaves open the possibility of the host tampering with either the code itself, or the outgoing HTML and Javascript, to evil ends.

What if a rogue administrator from the hosting company decided to quietly add a little monitoring function to a Javascript file, and append its execution to a window.onload function. Then, the evil monitoring function could be made to run once a minute within the browser. It might for instance, serialise the entire DOM, and upload a summary back to the server with ???, where more malicious code will log it somewhere convenient. A third-party hacker sitting between the browser and the server could also inject a script, and could upload data to a remote site with one of several established techniques. For instance, exporting browser data as CGI variables on the source URL of an external image under the hacker's control. In both scenarios, the application can continue as normal, the poor user none the wiser.

The threat of script injection certainly weakens the claim for this pattern, but doesn't invalidate it altogether. While script injection is theoretically possible, it does require some skill on the host's part and is also detectable if you know what the code should and should not be doing. If you happened to discover your application uploading DOM details every sixty seconds (using a tool like those described in “Traffic Sniffing”), there's a good chance that something's blatantly wrong.

As a further practical consideration, consider what happens if a hacker gains unauthorised access for a short time. They might well grab as much data as possible, but the data will be safely encrypted. In the unlikely event they were sophisticated enough to use script injection, they would only be able to gain pass-phrases of users who happen to be logged in during the time the hacker controls the server.

So pragmatic considerations suggest that the technique is safer than hosting the data in plain form, though by no means perfect. Is it so much safer to warrant the extra performance overhead, coding effort, and the constraint of zero page refreshes? That's a decision you'll need to make on a case-by-case basis, bearing in mind the criticality of the data, the likelihood of the various types of attack.

In theory, there's an even stronger claim in favour of this approach. It might be possible to develop a general-purpose plugin precisely for detection of script injection. For a given application, such a plugin would have access to a certified copy of the source code. Then, it could monitor traffic and caution about any unexpected activity. If such a plugin could be developed, the only way for script injection to succeed would be a conspiracy between the host, the code certifier, and the plugin manufacturer.

Decisions

What encryption algorithm and framework will be used?

You'll need an algorithm that's available in Javascript as well as your server-side environment. If the data is accessed by other clients, they obviously must have access to the algorithm too. A search reveals several algorithm implementations are available, each with their own strengths and weaknesses. All of the following are open-source.

When will the pass-phrase be requested?

The browser will need to query for the pass-phrase as soon as encrypted data must be rendered. However, that might not be immediately. To make the encryption less intrusive, you might consider using something like the “Lazy Registration” pattern, where regular data is shown as soon as the user accesses the application, with encrypted data only accessible after the pass-phrase has been entered.

Real-World Examples

There are no public real-world examples to my knowledge. One precedent is Hushmail, which uses a Java applet to allow access to email encrypted on the server.

Code Example [Host-Proof-Hosting Proof-Of-Concept]

Richard Schwartz provides a proof-of-concept demo (Figure 1.92, “Host-Proof Hosting”). For encryption, it delegates to a Javascript Blowfish library. The application accepts a pass-phrase, a message key, and some message content. It then encrypts the message and uploads it. It also uploads the key along with an encrypted version of the key, which can be used later on to check that the user has the correct pass-phrase. The application then shows how the server is holding only the encrypted content and the key. You can then pull the encrypted content back down and decrypt it with the original pass-phrase.

Figure 1.92. Host-Proof Hosting

Host-Proof Hosting

The application itself is quite simple: it presents a series of forms by manipulating their "display" style settings, to show and hide them. Thus, the user's pass-phrase remains in the pass-phrase input field at all times. This is the important thing about the demo: there's no form submission, so the pass-phrase only needs to be entered once.

When the application's ready to upload the encrypted data, it sets up the global variables required by the Blowfish library (ideally, the library would accept these as parameters). encodetext() is called (with "2" to specify the Blowfish algorithm) and it outputs the encrypted version in the form of a global variable:

    saveCryptoText() {
      ...
      inpdata=window.document.inputForm.plaintextInput.value;
      passwd=window.document.inputForm.password.value;
      // invoke blowfish
      encodetext(2);
      data = data + "&check=" + outdata ;
      ...
    }

The encrypted key is also attached:

    inpdata=window.document.inputForm.check.value;
    encodetext(2);
    data = data + "&check=" + outdata ;

The data can now be sent over to the server [19]:

    url = "http://smokey.rhs.com/web/test/AjaxCryptoConceptProof.nsf/SaveBlowfishDoc?OpenAgent" + data

    httpPost(url)	

Later on, the application provides a list of message keys that have been sent to the server. When the user chooses one, the application requests an XML document from the server, containing the message and key details (in practice, the message key could be verified first, before the body is downloaded). It first performs a check that the key is valid for this pass-phrase, then decrypts the message itself. Finally, a DOM object is morphed to display the decrypted text:

    inpdata = "";
    inpdata = getElementText(valuenode);
    outdata = ""
    decodetext(2);
    ...
    window.document.all.decryptedText.innerHTML = stripNulls(outdata);	

Alternatives

You might consider exploiting the increased permissions of a “Richer Plugin” to access a local data store. However, there is still a risk of malicious script injection from the server. Furthermore, you lose several key benefits of holding the data server-side:

  • Users will not be able to access the data from remote locations.

  • Each local data store will need its own backup process. Uploading to a backup server will defeat the purpose of the local store.

  • Each local data store must be protected against unauthorised access.

  • The data stores will sometimes need to be migrated as the system changes.

Another application of “Richer Plugin” would be to hold the data server-side, but use the “Richer Plugin” to manage the local encryption and decryption, in place of the Javascript code. A plugin like this could also retain the pass-phrase.

Related Patterns

“Direct Login” also involves using Javascript for encryption-related activity.

Apply a “Timeout” to clear the key from browser state after an idle period. This will help prevent unauthorised users from accessing the encrypted data.

Metaphor

Ceasar wonders whether he can entrust his aide to retain the top-secret recipe for victory wine. Fortunately, he's a pioneer of cryptography, so he just hands over the recipe in encrypted form.

Want to Know More?

Acknowledgements

Richard Schwartz's blog entries provided the idea for this pattern and the name is attributed to Richard, Michael Griffes, and their colleagues at eVelocity. I am also grateful to others who have commented on the approach, notably Alex Russell who has cautioned on the vulnerabilities of this approach, such as script injection.



[19] RESTful principles suggest the data would be uploaded with a POST in practice, because it will affect the server state. Another reason to use POST is that it's not practical to encode an entire message in the URL. However, this application is a proof-of-concept, so GET is used here instead.

Live Wiki Version for Host-Proof Hosting