Monday, March 8, 2010

Apache Tomcat - changing password stored in a MySQL database


One of the websites at work runs an Apache Tomcat server for a data trending application. I have been using a DataSourceRealm for authentication, with a MySQL database setup as Tomcat requires it; i.e. a "users" table with two columns -- username and password, where the stored password is an MD5 digest/hash of the user's password.

The docs for setting authentication up weren't great, but that's another story... In any case, the docs don't provide any way for you to change the stored password for a user. Up until recently, I've had to change the passwords by "manually" updating the database.

I just spent the better part of two days figuring out how to allow the users to change their own passwords, through a webpage served by Tomcat. This basically consisted of a few pieces I had to string together:
  • Configuring MySQL
  • Accessing MySQL via JSP
  • Generating MD5 hashes in JSP
  • Configuring the Tomcat application

Configuring MySQL
For security reasons, we don't want everyone in the world being able to read the whole user database, nor do we want them writing to entries that don't belong to them. I already had a read-only user setup for Tomcat so that it could do authentication. Granting additional privileges to that user would be a mistake -- what we want here is that the authentication process is read-only. Only after a user is authenticated to we want them to be able to write to the database.

Therefore, I created a separate MySQL user, with SELECT and UPDATE privileges. Tomcat of course must be configured appropriately, using a separate JDBC resource (see the "Accessing MySQL via JSP" section).

Accessing MySQL via JSP
There are a ton of web pages showing you how to embed Java code that accesses MySQL in your JSP. But it is actually a lot more simple if you use the JSTL tag library -- specifically, the sql tag. Basically, you do two things: 1) Configure the JDBC Resource in the "context.xml" file for your Tomcat application (alternatively, create a new XML file in conf/Catalina/localhost/) , and 2) use "sql" tags to query or update the database.

This is actually demonstrated nicely on the Tomcat Wiki for using DataSources, specifically, in the war file attached to that topic. Of great importance are two files included in the datasourcedemo.war -- jslt.jar and standard.jar. Supposedly you can get these directly from Sun, but I wasn't able to find them on Sun's web site. If you know where to get them, please comment!

Of course, getting a demo of MySQL access working is a far cry from having an application that changes a password. First, the simple stuff -- an HTML form to allow the user to input the old and new password (plus a confirm field for the password). There's no SQL code in this first page.

Note that I'm not asking the user for their username -- I'm using request.getRemoteUser() to get user's login name from the server.

Download password.jsp

And below, the code which the above page calls when submitting the form. This is the code for actually changing the user's password. Note again that I'm using request.getRemoteUser() for the user's login name. We should NOT trust the user's form submission to tell us the correct username. So, even if some malicious person submits their own form data for the username, the server will ignore it (I think...). Along similar lines, note the use of parametrized SQL queries, to prevent SQL injection attacks.

Also note the code at the top for including the JSTL tag library, and for importing the java security and io libraries.

The code for generating the MD5 hash I got from this page.

The code first uses an SQL query to get the row matching the user and hashed password. If the query returns a row, the c:forEach structure (from the JSTL core) executes the sql:update code to change the password.

I use a counter variable to decide whether to display a success or error message. The counter only increments if it enters the forEach loop -- which it only does if the SQL query is successful (i.e. the user entered the right old password).

Download change_password.jsp

Configuring the Tomcat application
I basically took the datasourcedemo.war and added my two JSP pages, then deployed the application on my Tomcat server to /changepass. I had to add security stuff to the WEB-INF/web.xml file, so that the changepass application was protected via the authentication already defined on my site. Of course, I had to edit META-INF/context.xml so that the MySQL resource was defined correctly for my system.

I think the most interesting/frustrating thing here was that I originally had the url-pattern in the security-constraint section wrong. Instead of just putting "/*", I had the name of the application -- "/changepass/*". This turned out to be a Bad Thing! Not only did it fail to protect the application -- it also somehow prevented the SQL code from working!

Download web.xml

All in all, two full days of work for me. Most of that was figuring out JSP syntax (which I had never done), and braving the horrible tangled mess that is Tomcat.

No comments:

Post a Comment