Monday, March 28, 2011

Friendlier Session Timeouts 2.0

Links to this post
As we discovered today, coding up friendly session time-outs involves more than meets the eye. As you know, a session time-out logs the user out after a period of inactivity. But interactions with web sites, and "inactivity," have changed over the last 10-15 years.

We have a fairly plain Rails app, and we're implementing a series of security fixes in anticipation of an audit. In what seemed like a simple "1-pointer", we were asked to remove the "Remember Me" checkbox from our application, and force the user to be logged out after 20 minutes.

For the first pass, we simply set the session timeout to 20 minutes and removed the checkbox (and the underlying implementation). Easy eh?

Sadly, this solution leaves much to be desired. When the user (or Q.A. engineer) returns after 1/2 hour, the page is unchanged and ready for action. But any click redirects, confuses, and potentially loses in-progress work.

Although this might have be acceptable in 1998, it's 2011 and we got Ajax. Clicking a link on a Web 1.0 site takes you to another page; if you happen to be logged out, oh well, sign back in and continue-- you probably weren't doing something that important anyhow.

But these days, on an Ajax page, if your session expires while you're viewing the page, clicking on any element of the page can reveal a session timeout. This may appear to the user as a server error, or if it's handled correctly (like we did), a page redirect. Clicking on a disclosure triangle redirects to a new page? Now we have a surprised user.


I Googled a bit and found lots of bug reports around this behavior. As this Jira snapshot shows, resolving this might be trickier than anticipated.

I checked out my bank's solution. It looked like they implemented a whole timeout scheme in Javascript. Were they wacko? (No... but we'll get there.)

Of the hand full of ideas out there on the web, here's one solution (of a hand full) with the idea of timing the session in Javascript:

<script language="javascript">

function confirmLogoff() {
  if (confirm("Your session will end in one minute.\n\nPress OK to continue for another ten minutes.")){
  location.reload();
  }
}
setTimeout("confirmLogoff()", <%= sessionTimeout - 1000*60 %>);
</script>


At first blush this looks like they might be on to something. Alas no. The confirmation will only work the brief period between the client and the server timeout. I'd argue it's even worse than no solution, since the messages promises something it can't deliver on, and loses the user's work-in-progress.

Did I mention we have quite a few pages with AJAX requests on them? These conveniently extend the session timeout on the server when the user interacts with them. But they break any sort of client-side timer that is set up at page load. Any sort of "meta refresh" schemes doing something similar to the above were quickly dismissed.

And why should just AJAX backed behaviors restart this timer? Opening a hidden panel may or may not go to the server (depending on an developer's whim), but should this whim this really affect the user's timeout? So we started considering implementations that ping the server as the user interacts. This was also dismissed as being complicated to implement efficiently (and potentially introducing some sort of security issue).

Finally where we "settled" (as in prom date), is implementing a timeout within Javascript, like my bank. It's a little more sophisticated: it's reset not only by the initial page load, but all sorts of user interactions. The code finally reduced down to:

var clientSessionTimeout = function(timeoutMS, logoutFn) {
  var lastTimeout;

  var startSessionTimeout = function() {
    if (lastTimeout) clearTimeout(lastTimeout);
    lastTimeout = setTimeout(function() {
          logoutFn();
      }, timeoutMS);
  };

  // Watch for activity
  $('body').click(startSessionTimeout).keydown(startSessionTimeout);
  startSessionTimeout();
};


This is called with the 20-minute timeout, and implements it beautifully:

    clientSessionTimeout(20 * 60 * 1000, function() {
      document.location = '/timeout?return_to=' + document.location.href;
    });


The server side timeout is for redundancy. Our pages are all pretty focused, so I doubt any user will spend more than a couple minutes on any one of them. We picked a server-side session timeout of 40 minutes. The only way the Javascript timeout won't kick in first is if the user interacts with the page for more than this time with no server side interaction... possible, but not likely.

After completing this compromise solution, I'm ready to spell out some ideal requirements:
* session timeout with no interaction after 20 minutes
* any interaction on the page should reset the timeout
* warn the user (if possible) when the deadline approaches
* this shouldn't open additional security vulnerabilities or server traffic

With some additional work, I'm sure an "ideal" solution can be developed. This compromise should get us most of the way there. Thanks to the rest of my team, and an interview candidate who provided some clear thinking on the matter.

Friday, March 18, 2011

Curing Frequent Selenium File Upload Failures

Links to this post
The symptom was quite simple: do an upload, and on the next request the server reports an "IOError". As our Ruby on Rails app is pretty much thin workflow around lots of file uploads, this was a problem. We tended not to see in on production, but us frequent users were seeing it enough to know we had to do something about it.

But the real complainer was Selenium. About half the time the tests failed and needed to be coaxed into running again.

JWinky traced the root cause down to a known bug in the temp file class. With a little work (and encouragement by yours truly), he put together a patch that has eliminated the problem. We've been running with it for a couple months and haven't seen the bug once-- or heard a peep from Selenium.

It's found here: http://github.com/jwinky/ruby_tempfile_ioerror

Tuesday, March 1, 2011

Updated Cheat Sheets

Links to this post
Five years ago, I was working at Great Schools, and got interested in SEO. I started running all sorts of experiments on my own site, to understand how I could affect things. I reorganized the URLs, added keywords, and followed all the standard recommendations. I quickly realized little tweaks to URLs, meta tags, and optimizing keyword density wasn't going to help much. These types of changes really are "optimizations"-- they'll give you a small percentage increase, but they are not game changers. If you've got millions of visitors, a 1% may mean real money, but if you're me, it doesn't matter.

So, after working on SEO, I pursued another idea. Why not create something of real value to drive people to my site? I had an idea and created some "cheat sheets" to help me with my own development. I created them, and then posted them where I could to get some inbound links. Shortly thereafter, someone at O'Reilly found my page and linked to it, and all of a sudden I was getting hundreds of page views per day. So that was my lesson: If I provide something of value, people will come. That was five years ago. Even though technology changes fast, I still have a bit of tail from those original cheat sheets.

Last night, I decided that since of 80% of the people hitting my site are seeing those pages, I should take a look at them and see what impression they might be making. I really don't have a "goal" of driving traffic anywhere else, but I might as well make them look as good as I can. So I cleaned up the visual design and fixed some of the editing.

Check out the spruced up pages here: Hibernate Mapping and JSPx Cheatsheet.