I was recently reading Spring Security 3 and it made an interesting point about some LDAP servers.
The servers never provide the (hashed) password.
That raises the immediate question of how you can possibly authenticate the user. The answer is straightforward – the underlying database query becomes
select prop1, prop2, prop3 from users where username='user' and password='hashed.password'
instead of
select * from users where username='user'
This is hard to enforce in a standard database unless you use stored procedures or, maybe, use views and column-based access permissions. The former would work well, the latter would undoubtably be fragile and quietly relaxed by someone who “didn’t see the harm”.
However the benefits of this approach should be obvious. If an attaker is limited to SQL injection the first approach prevents attackers from seeing the hashed passwords (provided the column is properly protected). The second approach does not. Of course this is not a panacea since there are many other attack vectors.
How do you handle hashing?
This approach immediately raises the question of how you handle hashing. There are three immediate possibilities.
Use a system-wide salt. This is the easiest approach but it’s also the weakest.
Use a hybrid username-based/system-wide salt. Salts based on usernames alone are fairly weak since they’re easy to guess. Hybrid salts based on usernames and a system-wide salt are stronger. A minimum salt would be H(username.secret).
Use a random salt. Random salts are the strongest but they require two queries. The first gets the salt by username, the second gets the rest of the data by username and hashed password.
In all cases the standard hashing rules apply. If you use a different app, e.g., LDAP, to manage the authentication information then you should do whatever it expects. If you’re rolling your own you should use a trusted library like bcrypt or modern hashing techniques.
A quick hand-wave of the latter:
- byte[] hash(String password, String username, String secret) {
- byte[] salt = crypto.hash(username + secret);
- byte[] hash = crypto.hash(password + salt);
- for (int round = 0; round < 1000; round++) {
- hash = crypto.hash(hash ^ salt);
- }
- return hash;
- }
byte[] hash(String password, String username, String secret) { byte[] salt = crypto.hash(username + secret); byte[] hash = crypto.hash(password + salt); for (int round = 0; round < 1000; round++) { hash = crypto.hash(hash ^ salt); } return hash; }
The multiple hashes are required since serious attackers now have access to complete rainbow tables and GPU farms.
LDAP
Going back a step – why does the spring security book discuss LDAP?
There are several reasons. First, there are now several good embedded LDAP implementations. It can be a separate .rar in your .ear file, it could even be a single .war file. This should be no more surprising than an embedded database implementation like Jetty or H2.
Second, the implementation has already been written. You have to configure it, of course, but you don’t have to write any code.
Finally, it integrates with enterprise systems. This isn’t an issue with a public-facing site but is very important with internal sites.