Securing Web-application state stored on the clients and a lesson in ease of development using cryptography
Web applications can store their state on the client to reduce the server-side overheads, as well as solve problems like navigating through the browser back button. We wrote about the benefits and risks of storing state on the client in an FAQ entry on the Java BluePrints website a couple of years back. Recently, Greg Murray and I developed a reusable JSP tag and associated Java classes that JSP developers can use to store state on clients securely. This solution is described in detail in the Java BluePrints Solutions Catalog and Greg recently wrote a blog on the topic. In this blog, I will continue that discussion to present the various security aspects of our solution. I will also discuss a tip that the crypto library writers will, hopefully, use in the future to make crypo easy-to-use by regular Java developers.
The state that needs to be stored on the client is first serialized into a byte array. This byte array is then encrypted using industrial-strength 3DES with cipher-block-chaining (CBC) and an initialization vector. To make the content tamper-resistant, we then create a message authentication code (using HmacSHA1 algorithm) from the encrypted content and the initialization vector. The initialization vector, the message authentication code, and the encrypted content is then combined in a single byte array. Finally, this resulting byte array is converted into a base64 string which is stored in a hidden FORM field on the client.
This solution is secure because it uses a strong crypto algorithm and also uses a MAC for tamper-resistance. We also generate all the random numbers (for example, to generate the initialization vectors and the password) using the cryptographically-secure
SecureRandom class. Note that the encryption keys are NEVER sent to the client or sent on the wire. These keys are used only on the server-side to encrypt and decrypt the state. One challenge is to decide what encryption keys to use. The encryption keys should not be known to the client, but still be associated with the client. We solve this problem by generating these keys from a password that is generated randomly and stored in the HttpSession. This strategy for key generation through password is pluggable in our solution and can be changed if needed.
Now to the lesson in ease of development: Why is the javax.crypto library so hard to use? To build my solution, I had to learn so many esoteric concepts like the various KeySpec classes, SecretKeyFactory, IVParameterFactory and so on. Note that this is over and above the crypto concepts that I needed to learn such as encryption algorithms, MAC algorithms, encryption keys, secure random, and initialization vectors. A user should NOT need to learn any of these concepts for most common uses of crypto. Ideally they will use a utility class like the one I wrote: ByteArrayGuard. The users of ByteArrayGuard just call the
encrypt method passing in a byte array, and they get back a byte array of encrypted content. Similarly, for decryption they just call the
decrypt method passing in the encrypted byte array and get back a decrypted byte array. I hope a future version of the crypto package will include such convenience classes or mechanisms so that crypto can be used (with default selection of algorithms) by regular Java programmers easily.
The source code from Java BluePrints Solutions Catalog including this reusable JSP tag library is available under a BSD style license. You are welcome to use it royalty-free.
I learnt all the cryptographic concepts from an excellent Stanford class CS255 taught by Dan Boneh. I highly recommend that class if you would like to learn more about cryptography.
What do you think about our solution? How can this reusable tag be improved? Is storing encryption passwords in HttpSession a good strategy? If not, how will you do it? And how about the crypto package? Do you think that it is too hard to use? If so, what are your suggestions for simplifying it?