Feature: support bcrypt, chained hashes for passwords, ... in JDBCAuthProvider

Proposed in PR: JDBCAuthProvider: adding support for bcrypt and more by trumpetx · Pull Request #390 · igniterealtime/Openfire · GitHub

The following (related) features have been added to the JDBCAuthProvider:

1) BCrypt support for passwords

Just like MD5/SHA###, JDBCAuthProvider can now lookup and compare bcrypted password hashes (or more accurately, the plain text password to a bcrypt hash). Furthermore, password resets will support bcrypt as well (if configured).

2) “Chained” password hashing (still defaults to simply “plain”)

As happens sometimes with “legacy” systems, password hashes are deemed “bad” over time. Imagine when you started your current job and saw plain MD5 hashes sitting in the “password” field of your users table. Your first though was probably “We should use SHA256 (or whatever)”, but instead of simply resetting everyone’s password you simply perform a SHA256 hash on top of the MD5 hash and modify your code accordingly. Then, you decide to integrate Openfire to your database. Ack! your passwords aren’t simply SHA256’d, they’re MD5’d then SHA256’d!

Enter chained password hashing. Simply configure the “jdbcAuthProvider.passwordType” param to be a comma separated list of valid values. With a new configuration of “md5,sha256”, instead of simply comparing the submitted password to MD5(“password”), JDBCAuthProvider will compare the database stored hash to SHA256(MD5(“password”)).

One caveat: when using bcrypt - it needs to be the “last” in the chain. BCrypt is pretty cool as it creates a different hash every time. Instead of comparing HASH_ALG(“password”) == PASSWORDHASH, you call a BCrypt method to mathamagically figure out if the plaintext password could create the given hash that is provided.

3) Submission of hashes in lieu of passwords (defaults to false)

Here’s the situation: You’re integrating Openfire into your application and logging users in via your application using something like Smack. You’re a responsible developer so you have NO IDEA what the user’s password is. You forgot it as soon as you could and only ever stored it in a char[] (good for you!) Now you need to log your user into Smack. Sure, you can go write your own custom JDBCAuthProvider, but wouldn’t it be neat if you could simply configure the perfectly good one to do what you need? Well, now you can tada.

As mentioned in the Javadoc, this feature is the logical equivalent of:

  • jdbcAuthProvider.passwordSQL = SELECT MD5(password) FROM user_account WHERE username=?

  • jdbcAuthProvider.passwordType = plain

-OR-

  • jdbcAuthProvider.passwordSQL = SELECT password FROM user_account WHERE username=?

  • jdbcAuthProvider.passwordType = md5

Why not simply configure for the former situation? Well, then the user would need to know the password hash to log in via the web console or the user wants to log in to chat directly with Adium. (And wouldn’t you know it, the web console input truncates password length shorter than a bcrypt!)

1 Like

Thanks, filed as [OF-975] JDBCAuthProvider: add support for bcrypt and more - Jive Software Open Source

(1) Seems OK, although I always worry that transmission of plaintext equivalents over the wire is never a very good thing, even if the verification store is well protected and hashed.

(2) Seems great. Hash agility is always a pain, and always a good thing. Might be nice to be able to extract the password type from the store, though I’m not that bothered - the risk of having an older hash collide with a newer one is quite small, since the length tends to vary. I’d like that verified for BCrypt, mind.

(3) Isn’t this the logical equivalent of downgrading your password store to holding plaintext equivalents? I think this is really bad. I appreciate it defaults to false, but still.

Great points ((I’ve considered them all too, let me explain).

  1. This is the current status quo. I believe the “right” method to combat this issue is to simply use https over http (or tls > direct socket). The issue here with not doing plain text over the wire is that your client needs to know the right way to hash/obfuscate the password before sending it over. Since Openfire uses many chat clients, this would be prohibitive.

  2. Auto-detection is neat and all, but it’s currently described explicitly and this is really just an extension of the current implementation (while adding bcrypt). Someone could add some auto-detection and use the logger to print some useful error messages, but in the end - if you’re using a JDBCAuthProvider, you should probably know how your passwords are stored.

  3. Short answer: yes. And I completely agree. However, as noted, you can currently do this already (by setting the type to plain); and, it is what I was doing because I’m in the exact situation provided in the explanation. I wracked my brain around how to get around the problem and I was left with 3 alternate solutions (if you have another solution, I’d be glad to consider it!):

  1. Remember my user’s password after they enter it. Of course, our application follows your #1 rule and we md5 the password before sending it over the wire (eventually bcrypted) so I’d have to remove that hashing and store the password in memory at least long enough to log in with the chat client (and then force them to re-enter a password if they logged out and back in). In the end, I think this is the least secure solution.
  2. Forego the opportunity for our users to use their own XMPP client (or our ability to log into the web console). We already lock down the server to 127.0.0.1 only (since we’re managing the web/Smack chat client directly). To do this, we simply make the type “plain” and then have the auth password selected by “select hashed_password from users where userid=?”. So, in effect, we’re doing the exact thing that is the logical equivalent of passing plain text passwords (aka, direct hash comparison) anyway at a downgrade to our user’s experience.
  3. Provide some sort of secret key where only we and Openfire know about it. Sort of like a master password. Of course, XMPP custom clients is completely out of the question if we did this, but it’s a quick way around the issue. (Something like: “select MD5(userid) from users where userid=?”) Of course, at that point, we might as well do: “select ‘passwordlolz’;”.
  4. Provide the proposed solution where a user can provide the hash OR the password directly. This allows our users to log in via their own XMPP client using the already existing mechanisms for password hashing. The security concerns about someone logging in with their own hash is pretty minimal IMO. In our case, the user can’t manage their master user account via XMPP - this is simply to access chat. If someone has hacked our database and stolen bcrypt hashes, access to chat is pretty much the least of our concern as it indicates they had access to a lot of other info far more dangerous than chat.

One idea I just had when typing this up was perhaps forcing password resets to false (we’re going to do this anyway) if “acceptPreHashedPassword” is true. But I’m not really a fan of “magic property changing” - even if it is good for a user. Best to let people make their own informed decision I think.

OK, so:

(1) Sort of. SASL mechanisms such as SCRAM neither store nor transmit a plaintext equivalent, but using BCrypt on the server precludes using them, resulting in potentially worse security, by some metrics.

(2) Yeah, I like this.

(3) This can be done using SASL proxy auth; so your (non-user) tools can authenticate using a different authentication identifier and credential, but request that they are authorized as the desired user. You can do all sorts here, including issue X.509 certificates only for the user which is allowed to authorize as anyone. Generally speaking, this can be pretty secure.

There is little point in hashing the passwords if the hashes are themselves acceptable as passwords. The only remaining reason is to protect the passwords in case of password reuse on other systems.

The bottom line is I’m fine with your first two features, but I’m not happy at all with the third - even if it’s some option that’s always turned off.

re 1) I’m not sure I’m following your logic here. Lets say I’m simply using an “as-is” JDCBAuthProvider. Could I use a SHA512 hashed password using the configuration:

authSQL = “select passwd_sha256_hashed from users where user_id=?”

type = SHA512

and still use SASL? From what I understand, this has the same issue as bcrypt, right? In which case your argument is really around how the JDCBAuthProvider works and not the addition of bcrypt to this. Unless I’m missing something (which is probably the case).

re 3) This sounds like exactly what I was looking for, but I didn’t know there was an alternate (aka, authenticate as a system vs. authenticate as a user) option in Openfire. I’m going to research and test this some more. Passing the hash directly was my method of “I don’t want to build a trusted link configuration”. If one is already available and I don’t have to build it, then by all means I’m down for using it. All that said, it’s not like this configuration isn’t already available simply by doing:

authSQL = “select MD5(password) from users where userid=?”

type = plain

I’ll have a good long read re: SASL. I hope it can fill this void.