It’s Friday, and time again for some Friday Fixes: selected problems I encountered during the week and their solutions.
Today’s post will be the last of the Friday Fixes series. I’ve received some great feedback on Friday Fixes content, but they’re a bit of a mixed bag and therefore often too much at once. So we’ll return to our irregularly unscheduled posts, with problems and solutions by topic, as they arrive. Or, as I get time to blog about them. Whichever comes last.
More Servlet Filtering
On prior Fridays, I described the hows and whys of tucking away security protections into a servlet filter. By protecting against XSS, CSRF, and similar threats at this lowest level, you ensure nothing gets through while shielding your core application from this burden. Based on feedback, I thought I’d share a couple more servlet filter security tricks I’ve coded. If either detects trouble, you can redirect to an error page with an appropriate message, and even kill the session if you want more punishment.
Validate IP Address (Stolen session protection)
At login, grab the remote IP address: session.setAttribute(“REMOTE_IP_ADDR”, request.getRemoteAddr()). Then, in the servlet filter, check against it for each request, like so:
private boolean isStolenSession(HttpServletRequest request) { String uri = request.getRequestURI(); if (isProtectedURI(uri)) { HttpSession session = request.getSession(false); String requestIpAddress = request.getRemoteAddr(); if (session != null && requestIpAddress != null) { String sessionIpAddress = (String) session.getAttribute("REMOTE_IP_ADDR"); if (sessionIpAddress != null) return !requestIpAddress.equals(sessionIpAddress); } } return false; } |
Reject Unsupported Browsers
There may be certain browsers you want to ban entirely, for security or other reasons. IE 6 (“MSIE 6”) immediately comes to mind. Here’s a quick test you can use to stop unsupported browsers cold.
private boolean isUnsupportedBrowser(HttpServletRequest request) { String uri = request.getRequestURI(); if (isProtectedURI(uri)) { String userAgent = request.getHeader("User-Agent"); for (String id : this.unsupportedBrowserIds) { if (userAgent.contains(id)) { return true; } } } return false; } |
Community Pools
In my last Friday Fixes post, I described how to use Apache DBCP to tackle DisconnectException timeouts. But as I mentioned then, if your servlet container is a recent version, it will likely provide its own database connection pools, and you can do without DBCP.
When switching from DBCP (with validationQuery on) to a built-in pool, you’ll want to enable connection validation. It’s turned off by default and the configuration settings are tucked away, so here’s a quick guide:
Servlet Container | Configuration Steps |
Tomcat 7 (jdbc-pool) | Add the following to Tomcat’s conf/server.xml, in the Resource section containing your jdbc JNDI entry:
|
WebSphere | Set the following in Admin Console:
Resources – JDBC – Data sources > (select data source) > WebSphere Application Server data source properties:
|
WebLogic | Set the following in the Administration Console:
JDBC – Data Sources – (select data source) – Connection Pool – Advanced:
|
Adjust intervals as needed for your environment. Also, these validation SQLs are for DB2; use equivalent SQLs for other databases.
Auto Logoff
JavaScript-based auto-logoff timers are a good complement to server-side session limits. Such are especially nice when coupled with periodic keep-alive messages while the user is still working. The basic techniques for doing this are now classic, but new styles show up almost daily. I happen to like Mint‘s approach of adding a red banner and seconds countdown one minute before logging off.
Fortunately, Eric Hynds created a nice jQuery-based Mint-like timer that we non-Intuit folks can use. It uses Paul Irish’s idleTimer underneath.
Dropping it in is easy; just include the two JS files and add the idletimeout div into your common layout. I tucked the boilerplate JavaScript into a simple common JSPF, like so:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <script src="<c:url value="/script/jquery.idletimer.js"/>"></script> <script src="<c:url value="/script/jquery.idletimeout.js"/>"></script> <script> var idleTimeout = <%= SettingsBean.getIdleTimeout() %>; if (idleTimeout > 0) { $.idleTimeout('#idletimeout', '#idletimeout a', { idleAfter: idleTimeout, pollingInterval: 30, keepAliveURL: '<c:url value="/keepalive" />', serverResponseEquals: 'OK', onTimeout: function(){ $(this).slideUp(); allowUnload(); window.location = '<c:url value="/logout" />'; }, onIdle: function(){ $(this).slideDown(); // show the warning bar }, onCountdown: function( counter ){ $(this).find("span").html( counter ); // update the counter }, onResume: function(){ $(this).slideUp(); // hide the warning bar } }); } |
Inanimate IE
Among Internet Explorer’s quirks is that certain page actions (clicking a button, submitting, etc.) will freeze animated GIFs. Fortunately, gratuitous live GIFs went out with grunge bands, but they are handy for the occasional “loading” image.
I found myself having to work around this IE death today to keep my spinner moving. The fix is simple: just set the HTML to the image again, like so:
document.form.submit(); // IE stops GIF animation after submit, so set the image again: $("#loading").html( '<img src="<c:url value="/images/loading.gif"/>" border=0 alt="Loading..." />'); |
IE will then wake up and remember it has a GIF to attend to.
Dissipation
The trouble with keeping your data in the cloud is that clouds can dissipate. Such was the case this week with the Nike+ API.
Nike finally unveiled the long-awaited replacement for their fragile Flash-based Nike+ site, but at the same time broke the public API that many of us depend on. As a result, I had to turn off my Nike+ Stats code and sidebar widget from this blog. Sites that depend on Nike+ data (dailymile, EagerFeet, Nike+PHP, etc.) are also left in a holding pattern. At this point, it’s not even clear if Nike will let us access our own data; hopefully they won’t attempt a Runner+-like shakedown.
This type of thing is all too common lately, and the broader lesson here is that this cloud world we’re rushing into can cause some big data access and ownership problems. If and when Nike lets me access my own data, I’ll reinstate my Nike+ stats (either directly, or through a plugin like dailymile’s). Until then, I’ll be watching for a break in the clouds.
Broken Tiles
I encountered intermittent problems where Apache Tiles 2.2.2 where concurrency issues cause it to throw a NoSuchDefinitionException and render a blank page. There have been various JIRAs with fixes, but these are in the not-yet-released version 2.2.3. To get these fixes, update your Maven pom.xml, specify the 2.2.3-SNAPSHOT version for all Tiles components, and add the Apache snapshot repository:
<repository> <id>apache.snapshots</id> <name>Apache Maven Snapshot Repository</name> <url>http://repository.apache.org/snapshots</url> </repository |
Hopefully 2.2.3 will be released soon.
Toolbox Linkapalooza
A few favorite tools I used this week:
- WebScarab – Nice Ethical Hacking tool. Among many features, it exposes hidden form fields for editing.
- Burp Suite – Similar to WebScarab; its proxy and intruder features make packet capture, modification, and replay a snap.
- Runner’s World Shoe Advisor.