JBoss 6 AS: Altering application security policy based on request parameters

sara.salem's picture
0
No votes yet

In this article we discuss how we can use JBoss 6 AS custom login module for altering the security policy of a web application to have different authentication mechanisms based on client request parameters which can be useful in systems integration scenarios, typical scenario is navigation between different systems with implicit authentication

Example

  • Assuming a web application deployed on JBoss 6 AS and using JAAS for authentication and authorization. The application DB contains the table "USERS" which maintains the hashed passwords of the users generated using MD5 hashing algorithm.
  • The web application is used by two different background clients (user or another system). If the request contains an attribute (plain_password=true), this means that the client is sending a plain password; otherwise it means that the client is sending MD5 hashed password.
  • For example, If the user "tom" has password "welcome", the USERS table shall contain this record:

    username passwd
    tom wLE3/i15JFnyb/djzORFdKW1qwM=
  • The application security policy should adapt its authentication mechanism based on this input. If the client is sending a plain password (welcome) and the request parameter "plain_password" is equals to true, the Login module shall hash this password before matching it with the DB value. But if the provided password is already hashed (wLE3/i15JFnyb/djzORFdKW1qwM=) and the request parameter "plain_password" is equals to false, the Login module shall match the given password directly with the DB password.

  • Application Security Policy

    The application uses the following JAAS policy configured in /server/default/conf/login-config.xml:

    LISTING 1

    1. <policy>
    2.   <application-policy name="my_app_policy">
    3.     <authentication>
    4.       <login-module
    5.         code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
    6.         flag="required" >
    7.          <module-option name="dsJndiName">
    8.             java:/MyDatabaseDS
    9.          </module-option>
    10.          <module-option name="principalsQuery">
    11.             select passwd from Users where username=?
    12.          </module-option>
    13.          <module-option name="rolesQuery">
    14.             select userRoles, 'Roles' from UserRoles where username=?
    15.          </module-option>
    16.          <module-option name="hashAlgorithm">
    17.             MD5
    18.          </module-option>
    19.          <module-option name="hashEncoding">
    20.             base64
    21.          </module-option>
    22.       </login-module>
    23.     </authentication>
    24.   </application-policy>
    25. </policy>

    This security policy allows the user to enter only the plain password. The login module then will hash the password using the MD5 hash algorithm and base64 encoding before matching it with the password in the DB, because the password in the DB is hashed.

    In this case if the client provides the hashed password (wLE3/i15JFnyb/djzORFdKW1qwM=), authentication will fail.

    There are 2 possible solutions for this problem. The first solution is based only on JAAS configuration, while the other requires writing code to alter the login module behaviour.

    Solution 1

    The first solution is based on the "flag" attribute of the login-module tag. As shown in [LISTING 1], this attribute has 4 possible values:

    1. required: the authentication will succeed only if the login module succeeds. If it fails, other login modules will be validated.
    2. requisite: the authentication will succeed only if the login module succeeds. If it fails, other login modules will not be validated.
    3. sufficient: if one of the sufficient login module succeeds, the authentication succeeds immediately without validating the remaining modules.
    4. optional: either the login module succeeds or fails, the remaining modules will be validated.

    We can configure 2 login modules, one with hashAlgorithm option and one without it, and use the "sufficient" flag for both of them as below. In this case both clients will be able to authenticate successfully if either of the 2 modules succeeds.

    LISTING 2

    1. <policy>
    2.   <application-policy name="my_app_policy">
    3.     <authentication>
    4.       <login-module
    5.         code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
    6.         flag="sufficient" >
    7.          <module-option name="dsJndiName">
    8.             java:/MyDatabaseDS
    9.          </module-option>
    10.          <module-option name="principalsQuery">
    11.             select passwd from Users where username=?
    12.          </module-option>
    13.          <module-option name="rolesQuery">
    14.             select userRoles, 'Roles' from UserRoles where username=?
    15.          </module-option>
    16.          <module-option name="hashAlgorithm">
    17.             MD5
    18.          </module-option>
    19.          <module-option name="hashEncoding">
    20.             base64
    21.          </module-option>
    22.       </login-module>
    23.      <login-module
    24.         code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
    25.         flag="sufficient" >
    26.          <module-option name="dsJndiName">
    27.             java:/MyDatabaseDS
    28.          </module-option>
    29.          <module-option name="principalsQuery">
    30.             select passwd from Users where username=?
    31.          </module-option>
    32.          <module-option name="rolesQuery">
    33.             select userRoles, 'Roles' from UserRoles where username=?
    34.          </module-option>
    35.       </login-module>
    36.     </authentication>
    37.   </application-policy>
    38. </policy>

    Solution 2: Using Custom Login Module

    The second solution is based on the "code" attribute of the login-module tag. We will update it with our own implementation of the login module which will behave differently based on the request parameters sent by the client.

    The client send an additional request attribute (plain_passowrd=false) to indicate that the provided password is hashed.

    We will alter the login module implementation to read this attribute and if its value is "false", it ignores the 2 options "hashAlgorithm" and "hashEncoding".

    LISTING 3 provided the implementation of our login module "com.app.loginmodule.ConditionalHashingLoginModule".

    LISTING 4 provided the security policy using our new Login module.

    LISTING 3

    1. package com.app.loginmodule;
    2.  
    3. import java.util.HashMap;
    4. import java.util.Map;
    5.  
    6. import javax.security.auth.Subject;
    7. import javax.security.auth.callback.CallbackHandler;
    8. import javax.security.jacc.PolicyContext;
    9. import javax.security.jacc.PolicyContextException;
    10. import javax.servlet.http.HttpServletRequest;
    11.  
    12. import org.jboss.security.auth.spi.DatabaseServerLoginModule;
    13.  
    14. public class TestFile extends
    15.               DatabaseServerLoginModule {
    16.  
    17.     public void initialize(
    18.                     Subject subject,
    19.                     CallbackHandler callbackHandler,
    20.                     Map sharedState,
    21.                     Map options) {        
    22.      
    23.             HttpServletRequest request = null;
    24.                         try {
    25.                                 request = (HttpServletRequest)
    26.                                 PolicyContext.
    27.                                 getContext("javax.servlet.http.HttpServletRequest");
    28.                         } catch (PolicyContextException e) {
    29.                                
    30.                         }
    31.            
    32.             String plainPasswordFlag =
    33.             (String)request.getSession()
    34.                             .getAttribute("plain_password");              
    35.                                
    36.                 Map<String,Object> newMap =
    37.                         new HashMap<String, Object>();
    38.                 newMap.putAll(options);
    39.  
    40.                 if (plainPasswordFlag != null &&
    41.                         plainPasswordFlag.equals("false")) {                          
    42.                     newMap.remove("hashAlgorithm");                        
    43.             }
    44.             super.initialize(
    45.                     subject,
    46.                     callbackHandler,
    47.                     sharedState,
    48.                     newMap);
    49.     }
    50. }
    • Line 17: we override the initialize function which provides the options to the login module
    • Lines [23 - 30]: this is how we obtain the client request object
    • Lines [36 - 38]: because the options Map is immutable, we can't change its state, so we clone it in another Map (newMap)
    • Lines [40 - 42]: the hashAlgorithm attribute is removed if the plain password flag is false
    • Line 44: the initialization for the parent class DatabaseServerLoginModule is invoked.

    LISTING 4

    1. <policy>
    2.   <application-policy name="my_app_policy">
    3.     <authentication>
    4.       <login-module
    5.         code="com.app.loginmodule.ConditionalHashingLoginModule"
    6.         flag="required" >
    7.          <module-option name="dsJndiName">
    8.             java:/MyDatabaseDS
    9.          </module-option>
    10.          <module-option name="principalsQuery">
    11.             select passwd from Users where username=?
    12.          </module-option>
    13.          <module-option name="rolesQuery">
    14.             select userRoles, 'Roles' from UserRoles where username=?
    15.          </module-option>
    16.          <module-option name="hashAlgorithm">
    17.             MD5
    18.          </module-option>
    19.          <module-option name="hashEncoding">
    20.             base64
    21.          </module-option>
    22.       </login-module>
    23.     </authentication>
    24.   </application-policy>
    25. </policy>

Add new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.
By submitting this form, you accept the Mollom privacy policy.