Wednesday, May 11, 2011
GlassFish 3.1 SecureJDBCRealm - Detecting failed logins.
What's there and what did I do?
You probably know the GlassFish JDBC Realm already. It's a quite comfortable way to use a database as a user back-end for your container security. If you configure your web application appropriate your can ensure, that only valid users hit your protected resources. The only thing you have to do is to configure your JDBCRealm and off you go. If you are not familiar with the topic so far, I suggest, you dig into the Java EE 6 tutorial a bit to get a basic understanding.
So far so good. But what happens in an environment where you have to take care of additional protection needs? Some typical ones? Encrypted passwords in your database. You could achieve this with setting a digest-algorithm (MessageDigest: e.g. MD5) and off you go. What about tracking failed logins? Here we go. That's most obviously nothing the standard JDBCRealm does. So, I was trying to build a more SecureJDBCRealm and add this feature.
If I already have an example, I am going to add my stuff there. So I started over looking at the code already there. The com.sun.enterprise.security.auth.realm.JDBCRealm is an excellent place to look at.
So I basically copied it (with remaining copyright-headers of course ;)) and added my own features. Beside the fact, that you need at last two more properties for your realm, you need some additional prepared statements to execute in order to be able to keep track of the tries a user did. And you also do need your own SecureJDBCLoginModule to call your realm.
The steps I took in a very high-level view:
0) Add two more params to the realm (user-tries-column and user-tries-max)
1) change the passwordQuery to include the new "sanity-check" for userTriesColumn + " <=" + userTriesMax
2) Add three new prepared statements triesReadQuery, triesUpdateQuery, triesResetQuery
3) Change the isUserValid() method to include the test if more tries are available for a given username and add some logic for handline the tries colum (increment and reset)
4) I added a new public property to the realm to be able to getTriesLeft() for a given user.
5) implement the SecureJDBCLoginModule
Great. That basically was it. Now you have a new column mapping in your login module to track the not successful login attempts a user does. To be honest, this is not high performance in general. And using the programmatic request.login() with it's simple ServletException that is thrown is not very convenient. So even if you use this login module, you still have to find a way to tell the user about it's left tries and what he possibly did wrong.
Try it out - and give feedback!
If you are willing to try it out: Here you are. I made the complete maven based project (GF 3.1) available for you on github. Use it as it is. Without any warranty. And don't blame me, if something goes wrong. If you have ideas or better approaches: let me know! Happy to discuss this a bit!