TheJach.com

Jach's personal blog

(Largely containing a mind-dump to myselves: past, present, and future)
Current favorite quote: "Supposedly smart people are weirdly ignorant of Bayes' Rule." William B Vogt, 2010

Spring Security: basic auth custom message passing

I don't like Spring Security. Nevertheless, it's what I'm using for an application, so I had to learn more about it than I cared for. Let me describe my scenario, and my wishes.

Using basic authentication means sending some sort of authentication token up to the server with each request. This has some downsides, notably sending the password in the clear (it doesn't matter if it's base-64 encoded) every request. While yes sending a session cookie or some other identifier has the same risk of being sniffed in the network, at least when someone hijacks your session they don't also know your password that you might use for other services. This is why sites should ask for the old password before letting you change to a new one.

I wanted to bypass sending the password, and instead pass a UUID. I'm also sending a hashed version of the password (just for the first login request) and its salt, by first hashing it in the same way the server hashes new passwords and letting the server hash the stored database hash with the sent salt and compare with the double-hashed password. I need the UUID because I want unique, persistent database connections for each session rather than each user. The application I'm working on allows for a user to be logged in from multiple places, indeed possibly from multiple users, and I want a separate, distinct DB connection for each of them even if they are using the same database username.

I tried several things at first, perhaps the most important one being implementing my own custom UserDetails class (extending the User class) to include the extra parameters I wanted sent up. This turned out to be a fruitless direction, as I discovered UserDetails was mostly for the users you pull from the database, not the information you get sent from a client.

What is the information sent from a client, anyway? Typically it's base64.b64encode(user:password). What I found, though, was this. If I sent my information in the form of base64.b64encode(user:doublehashedpw:salt:UUID) then the server-side Spring Authentication object puts the user in the right variable, yet stores the entire right side of a single colon split as the password variable. In other words:


// sent the string (after b64 decoding): "user:garbl3:salt:3492-328-etc"
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
auth.getName(); // user
auth.getCredentials(); // garbl3:salt:3492-328-etc


How do I validate? I have the following beans:


<bean id="daoAuthenticationProvider"
class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<ref bean="userDetailsService" />
</property>
<property name="passwordEncoder">
<ref bean="passwordEncoder" />
</property>
</bean>


My userDetailsService is an implementation of the JdbcDaoImpl class and acts as my application's connection manager, using the above authentication information to create DB connections and store them in a HashMap by the UUID, bringing them out again for subsequent requests. (They are also garbage collected for actual connection closing; the app simply has to call my own release_connection() to mark a connection as open in the case of multiple async calls from the same client.)

My passwordEncoder is the important part, though. It extends the simple ShaPasswordEncoder, and makes use of just one public method: boolean isPasswordValid(String encPass, String rawPass, Object salt). (Note I'm not actually using the salt object.)

Spring automagically calls this after it has retrieved the username and password from the database and compared the usernames. Now it passes the password from the database through encPass, and the password from the request (which was "garbl3:salt:3492-328-etc") into the rawPass parameter. I split the string into three parts (with some error checking if bad info was sent) and the function turns into


if (hexHash(parts[1] + encPass).equals(parts[0])) {
// ^salt ^dbpass ^pw_token
return true;
}


hexHash() is my own custom private function for performing the equivalent of Python's hashutils.sha256('str').hexdigest(), since Java's default does .digest() instead.

So with a username and password validating correctly, it continues through my application to the eventual call to a getConnection(), which in turn analyzes the username and split "password" data to either make a connection or return one in the HashMap. This puts a bit more work on the client to validate itself properly, but in the end I think it's a pretty good solution that doesn't require a lot of extra Java code. My first idea had me implementing all sorts of Spring classes like pre-filters and post-filters and ugh. This is much cleaner. If you want to see the project, check out our trunk.


Posted on 2011-03-12 by Jach

Tags: java, programming, security

Permalink: https://www.thejach.com/view/id/161

Trackback URL: https://www.thejach.com/view/2011/3/spring_security_basic_auth_custom_message_passing

Back to the top

Back to the first comment

Comment using the form below

(Only if you want to be notified of further responses, never displayed.)

Your Comment:

LaTeX allowed in comments, use $$\$\$...\$\$$$ to wrap inline and $$[math]...[/math]$$ to wrap blocks.