Storing Secure Session State on The Client
Posted by gmurray71 on May 17, 2005 at 05:03 PM | Comments (7)
A few years back I developed a custom tag library for The Java Pet Store to get around back button
issues when a user decided to change the locale of the Java Pet Store. This was made possible by
storing all the objects related to a specific request inside each HTML page using a application
specific custom tag library. This technique was later adopted by Java ServerFaces to store the
component tree information on the client.
In the BluePrints Solutions Catalog we
generalized this technique of using a JSP custom tag library to allow a developer to easily and
securely store state on the client. The following JSP fragment shows how simple it can be to securely store
session state on the client.
<cs:client-state action="word" beanName="sampleBean" secure="true"> <table> <c:forEach var="word" varStatus="status" begin="0" items="${sampleBean.words}"> <tr> <td> <input type="checkbox" name="word_${status.index}" value="${word}"> ${word} </input> </td> </tr> </c:forEach> <tr> <td> New Word: <input type="text" name="newWord" size="25"/> </td> </tr> <tr> <td> <input type="submit" value="Update List"/> </td> </tr> </table> </cs:client-state>
The client-state tag acts like a form tag and wrapping a form submission. In the case the attribute
"beanName" specifies a Java object (which implements java.io.Serializable) that is
stored on the client. The "sampleBean" must exist in the HttpServletRequest
scope with id "sampleBean". The "sampleBean" Java object is serialized and encrypted
when the request is dispatched to the JSP page and the client-state tag is encountered.
The resulting HTML page will look as follows.
<form method="POST" action="word"> <input type="hidden" name="beanName" value="sampleBean" /> <input type="hidden" name="sampleBean" value="rO0ABXNy
AC5jb20uc3VuLmoyZWUuYmx1ZXByaW50cy5jbGllbnRzdGF0ZS5TYW1w
bGVCZWFuhCyFzCR588MCAAJMAAhuZXh0UGFnZXQAEkxqYXZhL2xhbmcv
U3RyaW5nO0wABXdvcmRzdAAVTGphdmEvdXRpbC9BcnJheUxpc3Q7eHBw
c3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4
cAAAAAJ3BAAAAAp0AARncmVndAAFaW5kZXJ4" /> <table> <tr> <td> <input type="checkbox" name="word_0" value="greg"> greg </input> </td> </tr> <tr> <td> <input type="checkbox" name="word_1" value="inder"> inder </input> </td> </tr> <tr> <td> New Word: <input type="text" name="newWord" size="25"/> </td> </tr> <tr> <td> <input type="submit" value="Update List"/> </td> </tr> </table> </form>
|
Notice that the form above contains a hidden field with the name "sampleBean" and value containing
encrypted base64 encoded data. This is the serialized "sampleBean" object from the
HttpServletRequest scope.
Before handling a request from a page containing the client-state tag the state must be decoded from base64
and decrypted (if secure is set to "true").
A utility class is provided as part of the client-state tag library to facilitate the decoding and decrypting
as may be seen in the code sample below.
ClientStateDeserializer.deserialize(request,response);
This code above reconsitute the "sampleBean" Java object and place it in the
HttpServletRequest scope.
After processing the request the server-side component may dispatch to a page that contains the
same Java object. The end result is that the "sampleBean" object never exists outside
of the HttpServletRequest scope.
Storing state on the client has the following benefits:
- Scalability - A single server can support more clients. An increase in clients does not require more memory or database resources on the server.
- Back Button is not a Problem - All state is saved in the page making the back button no longer an issue. What you see in the HTML page is the Java object used to generate that page.
- Session Time-Outs not an Issue -
HttpSession time outs are not a problem.
Saving state on the client does not come for free. Here are some of the drawbacks:
- Computing Resources - There is a CPU cost associated with encoding/decoding and encrypting/decrypting of state for each interaction.
- Bandwidth - Since all the state related to a page is sent back to the server on each request there will be more data sent.
- Navigation must be from a form - The state is kept as HTML form hidden variables requiring
page to page navigation to be from a form though links could use JavaScript to performs a form submits.
- Browser Crashes - If the browser that contains the state crashes the state is lost.
What do you think about storing state on the client?
For a much more detailed description of the idea of client-side state management see:
https://bpcatalog.dev.java.net/nonav/webtier/clientside-state/frames.html
To download the client-state tag from:
https://blueprints.dev.java.net/files/documents/1713/14760/clientstate-installer.jar
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
The amount of data you are doing seems more reasonable for a cookie if allowed by the browser. Relying on individual forms is more error prone and expensive since you would have to write this in every form instead of maintaining just once. Cookies can also be manipulated by JavaScript allowing more then just the server side to interact with this if the encryption part is not required.
How are the back buttons not an issue? What if the state changed from page to page? I also would not never use for a serious component, just simple things like bookmarks or last viewed.
What I do like about this approach:
* Reduce session memory is definitely a good idea
* encrypted data
* Possible cross language support here since nothing to stop a PHP or .NET app from also getting this bean information
Posted by: smartinumcp on May 18, 2005 at 08:02 AM
-
True cookies are available on the client but they are limited in size to 4KB and keeping secure data in them would also be an issue in that JavaScript does not provide any encrypting APIs that I know about but you could use the Servlet API to write the cookie so long as the data is very limited.
As for the back button try the component out. You can add or remove data from the object that is serialized, navigate backwards or forwards, then add something else. The data you manipulate is what was on the page that was posted from. The state cannot change from page to page because it does not exist between requests. If you look at the sample application in the BluePrints Catalog I even set the session timeout to "0".
I agree that this solution is not great for all usecases and I would love to hear more ideas.
From a web developer's perspective getting used to the idea that data only exists in a page and that is needs to be re-consituted for each request takes a little getting used to. If you get a chance use the component a little and I'm sure you will like it.
Posted by: gmurray71 on May 18, 2005 at 10:53 AM
-
Each site can have up to 20 cookies so that's 80KB, a pretty good amount. Here's a site that implements encryption for JavaScript. http://home.versatel.nl/MAvanEverdingen/Code/
.NET must do something similar with the client-side versions though I'm sure not encrypted like this.
Posted by: smartinumcp on May 18, 2005 at 11:10 AM
-
I've actually been thinking about using this same pattern. What would you do for redirects? Is it safe to store the state in the query string? Do any browsers have lengths limits for URLs?
I guess you could store the state in server memory between a post and redirect. If an error occurs between the post and redirect, the user could simply press the browser back button and try again.
Posted by: crazybob on May 18, 2005 at 02:54 PM
-
Inder, who also worked on this solution has blogged about the encryption aspects of this solution in his blog. This is a great read if you want to get into the details.
Posted by: gmurray71 on May 18, 2005 at 04:02 PM
-
So in response to the question about keeping this state as a URL parameter and when the state exists: This solutions use HTTP POST to ship around data because the allowable URL length generally tops off around 2048 Characters (in IE) (the URL length is not standard across containers and browsers). The state is re-consituted into memory when the request is received by the server-side component (such as a servlet). The object is placed in the HttpServletRequest scope and once the request has been processed it no longer exists. If you are doing redirect using the request dispatcher within the same context you can also send the object as well.
Posted by: gmurray71 on May 18, 2005 at 04:12 PM
-
Definitely use POST for this. Some developers are complaining that GET requests are being called by web accelerators that cause actions. Well, duh, GETs are not meant to be actions.
Posted by: smartinumcp on May 19, 2005 at 06:45 AM
|