Titel-Logo
Projektstudien
TraceLogger
Basics of Cryptography
Custom JBossAS Login
The problem
SaltedPasswordLoginModule
Configuration
Example application
Salt and Hash Generator
Further improvements
Downloads
SOAP Webservice
Role Based Access Control
SaltedPasswordLoginModule

As a replacement for the originally provided DatabaseServerLoginModule the SaltedPasswordLoginModule supports the same basic options, that is dsJndiName, principalsQuery, rolesQuery and suspendResume. The (required) dsJndiName option identifies the datasource containing the tables for users and roles. The principalsQuery and rolesQuery options refer to the queries used to match the password and to retrieve the roles associated with the user principal. The suspendResume option indicates if any JTA transactions should be suspended during the necessary DB operations executed by the SaltedPasswordLoginModule. The SaltedPasswordLoginModule will assume Base64 encoding for the hashed password and the salt.

The class SaltedPasswordLoginModule overrides the following methods from its superclass UsernamePasswordLoginModule:

(i) void initialize(Subject subject, CallbackHandler callbackHandler, Map<String,?> sharedState, Map<String,?> options)
(ii) String getUsersPassword()
(iii) boolean validatePassword(String inputPassword, String expectedPassword)
(iv) Group[] getRoleSets()

The method (i) is declared at the top of the login module class hierarchy, this is indeed the javax.security.auth.spi.LoginModule interface from the JDK. The JBoss interpretation (referring to picketbox 4.0.21.Final) of this method dictates that in the first place we have to inform the container about the valid options for this module before calling the super class implementation. We are doing this by invoking the method addValidOptions(final String[] validOptions) defined within AbstractServerLoginModule. The container uses the fourth argument to pass in the configured options. Apart from evaluating the given options we have no further business with this method.

Method (ii) is responsible for the retrieval of the expected (hashed) password from the database together with the randomly chosen salt. The result type of the method is fixed - otherwise it wouldn't override its superclass implementation. That is we must the store the value of the salt - as retrieved from the database - in an instance member for later use. The return value of method (ii) is fed into the second parameter (expectedPassword) of method (iii) by the container. The default SQL statement which is used if no principalsQuery is present is shown below. This means that an account is identified by its name and can be disabled. A custom principalsQuery should use aliases to indicate the columns containing password and salt if required.

1 SELECT password, salt
2 FROM prinicipals
3 WHERE name = ? AND disabled = 'N'

We must consider the case that we didn't find any match in the database. An immediate login failure would lead to a shorter evaluation time than needed when evaluating a valid account. This would indicate an invalid or disabled account to an attacker. In order to eliminate timing attacks we note in this case that the account doesn't exist or is disabled in an instance member and return some default values for the salt and the expected hash.

As suggested by the name, method (iii) performs the validation of an inputPassword against an expectation. The value of inputPassword must undergo the same transformations that originally led to the value of expectedPassword. These transformations are shown below:

1 byte[] bytes = inputPassword.getBytes("UTF-8");
2 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
3 messageDigest.update(bytes);
4 Base64 codec = new Base64();
5 byte[] saltBytes = codec.decode(this.salt);
6 byte[] checkSum = messageDigest.digest(saltBytes);
7 String saltedInputPassword = codec.encodeToString(checkSum);

This means that we first applying the UTF-8 encoding to get the raw bytes of the inputPassword. Secondly we feed this raw bytes into the SHA-256 hash algorithm. Next we must transform the Base64 encoded salt previously retrieved from the database into raw bytes. Now we concatenate these raw bytes with the current state of the SHA-256 hash algorithm and obtain the final hash value raw bytes. We transform these raw bytes into a Base64 encoded string again and we are ready to perform our validation against expectedPassword. Following a Defense in Depth-policy we take care about potential timing attacks. Therefore the final validation will be done in constant time, that is all characters will be compared regardless of early matching failures. Otherwise an attacker could conclude from the execution time how many bytes at the beginning of the checksum do match.

Method (iv) retrieves the roles associated with an authenticated user. We are only interested in one Group named "Roles". That is method (iv) will always return an array with one such Group element. This Group element contains principals which correspond to the roles owned by the authenticated user. The default SQL statement which is used if no rolesQuery option is defined is shown below:

1 SELECT a.name AS groupname
2 FROM roles a, principalrole b, principals c
3 WHERE a.name = b.groupname AND b.username = c.name AND c.name = ?

That is the default statement assumes a many-to-many relationship between user principals and roles. A custom rolesQuery should always define an alias 'groupname' for the column containing the roles within the query if required.

Valid XHTML 1.0 Strict