4

What is the WildFly (8.2) way to work with randomly salted passwords stored in a database?

Is an implementation of org.jboss.crypto.digest.DigestCallback (in the password validation process) meant to have access to the salt part from the database?

Or should I simply hash and salt passwords by my self before handing them over to the login method of HttpServletRequest?

Jens Piegsa
  • 7,399
  • 5
  • 58
  • 106

1 Answers1

5

It looks to me like the 'WildFly way' to deal with passwords is to do what most containers do and deliver a non-secure solution out-of-the-box. I don't know why, but every standard JDBC realm implementation I've seen so far just hashes the passwords without salt... Which is totally insecure.

Open Source solution

EDIT: I found an out-of-the box solution that works on WildFly. I ended up using it myself and it works well. I can recommend it:

m9aertner/PBKDF2

Here is how I configured it:

First add a module to WildFly by creating a folder below modules/, like this:

C:\WildFly\v8.2.0\modules\de\rtner\PBKDF2\main

Place the PBKDF2-1.1.0.jar file in it along with a module.xml with these contents:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="de.rtner.PBKDF2">
  <resources>
    <resource-root path="PBKDF2-1.1.0.jar"/>
  </resources>
  <dependencies>
    <module name="org.picketbox"/>
    <module name="javax.api"/>
  </dependencies>
</module>`

Then, add a realm configuration to standalone.xml:

<subsystem xmlns="urn:jboss:domain:security:1.2">
  <security-domains>
    <!-- .... -->

    <security-domain name="MyRealm">
      <authentication>
        <login-module code="de.rtner.security.auth.spi.SaltedDatabaseServerLoginModule" flag="required" module="de.rtner.PBKDF2">
          <module-option name="dsJndiName" value="java:/jdbc/MyDS"/>
          <module-option name="principalsQuery" value="SELECT password FROM users WHERE username = ?"/>
          <module-option name="rolesQuery" value="SELECT roles.name AS groupid, 'Roles' FROM roles INNER JOIN user_roles ON roles.name = users.username WHERE users.username = ?"/>
          <module-option name="unauthenticatedIdentity" value="guest"/>
          <!-- DEFAULT HASHING OPTIONS:
          <module-option name="hmacAlgorithm" value="HMacSHA1" />
          <module-option name="hashCharset" value="UTF-8" />
          <module-option name="formatter" value="de.rtner.security.auth.spi.PBKDF2HexFormatter" />
          <module-option name="engine" value="de.rtner.security.auth.spi.PBKDF2Engine" />
          <module-option name="engine-parameters" value="de.rtner.security.auth.spi.PBKDF2Parameters" />
          -->
        </login-module>
      </authentication>
    </security-domain>

    <!-- .... -->
  </security-domains>
</subsystem>

The SQL queries are the same as for the standard DatabaseLoginModule. The default hashing options need not be specified (as they are default) but you do need to be aware of them (and set them correctly) when creating new users in order to hash their password correctly with the same parameters.

Example use

Here is what I am doing in my code to create a new password hash (including salt) based on a given plaintext:

public static String hash(String plainText, String storedPassword) {
    if (plainText == null) return null;
    SimplePBKDF2 crypto = new SimplePBKDF2();
    PBKDF2Parameters params = crypto.getParameters();
    params.setHashCharset("UTF-8");
    params.setHashAlgorithm("HmacSHA1");
    params.setIterationCount(1000);
    if (storedPassword != null) {
        new PBKDF2HexFormatter().fromString(params, storedPassword);
    }
    return crypto.deriveKeyFormatted(plainText);
}

When creating a new password, you would call this function passing null as the storedPassword:

String password = hash('MySecretPassword', null);

password would end up looking something like this:

"192EAEB3B7AA40B1:1000:4C137AF7AD0F3999D18E2B9E6FB726D5C07DE7D5"

When comparing passwords, you call the same function, passing the original password and then compare the results:

String enteredPassword = hash(userInput, password);
if (enteredPassword.equals(password)) {
    // Ok!
}

The reason you need to pass the original password is that the hashing parameters and salt are stored in the password hash, so the algorithm needs the stored password to get these parameters and use them for the new hash. However you don't usually need to compare passwords yourself as this is already done in the login module.

Or, roll your own

This blog post gives some explanation on how to roll your own Realm implementation that does add salt. He has source code on GitHub so maybe use that.

It's for Glassfish, but I think it doesn't matter as far as the Realm implementation code goes.

Stijn de Witt
  • 40,192
  • 13
  • 79
  • 80
  • How to implement in this solution password salts? – perotom Oct 28 '15 at 13:09
  • @user2630406 I added an example of hashing either a new password or user input to compare with an existing password. – Stijn de Witt Oct 28 '15 at 15:04
  • Is the salt stored in the PBKDF2 hash? In the example you add the salt before hashing but this is not working with PBKDF2. – perotom Oct 28 '15 at 15:23
  • @user2630406 I don't add the salt manually... It is done by the hashing algorithm. The parameters and salt can actually be clearly seen in the hashed password. I'll add an example result to my answer that will hopefully clear things up. – Stijn de Witt Oct 28 '15 at 16:06
  • That would be awesome! – perotom Oct 28 '15 at 18:11
  • @user2630406 I edited it in already. You can see the salt as the first hex-encoded sequence in the password string. It is followed by `:1000:` (which is the iteration count) and then the password hash itself, also hex-encoded. – Stijn de Witt Oct 28 '15 at 20:28
  • perfect example. Thank you very much! – perotom Oct 28 '15 at 21:15