Titel-Logo
Projektstudien
TraceLogger
Basics of Cryptography
Custom JBossAS Login
The problem
SaltedPasswordLoginModule
Configuration
Database setup
JDBC driver and Datasource
SSL setup
Security Domain
Web descriptor
Example application
Salt and Hash Generator
Further improvements
Downloads
SOAP Webservice
Role Based Access Control
Configuration - Preface

Container-managed security is something good because the developer can focus on what(!) resources have to be secured and not how this can be done. We call this declarative security. Unfortunately container-managed security requires a bunch of configurations. These configurations differ between various application servers and sometimes between different versions of the same server as well. This relativises the usefulness of container-managed security somewhat. The subsequent specified procedures have been tested with JBoss AS 7.1.1 and Wildfly 8.2.0. Recently RedHat seems to push its commercial EAP platform. That is RedHat doesn't provide compiled binaries e.g. for the JBoss AS 7.3 which corresponds to the 'hardened' EAP 6.2.0 commercial JBoss AS variant. You would have to download the sources and build the binary by yourself. Crosscheck the procedures with your documentation if you use one of the EAP 6.x.x or rather JBoss AS 7.2+ variants.

All specified procedures refer to a "standalone server" using the standalone-full.xml profile.

Configuration - Database setup

Since we want to authenticate against credentials stored in a (SQL-)database we need to create some tables containing the accounts and the roles. The Example application assumes the data definitions shown below. I'm using MySQL here but the DDL-statements are simple enough and should be roughly the same for other SQL-databases.

1 CREATE TABLE principals (
2 name VARCHAR(30) NOT NULL,
3 password VARCHAR(64) NOT NULL,
4 salt VARCHAR(128) NOT NULL,
5 disabled VARCHAR(1),
6 counter INT
7 );
8 ALTER TABLE principals ADD CONSTRAINT pk_t_prinicipal PRIMARY KEY (name);
9
10 CREATE TABLE roles (
11 name VARCHAR(30) NOT NULL,
12 description VARCHAR(100) NOT NULL
13 );
14 ALTER TABLE roles ADD CONSTRAINT pk_t_group PRIMARY KEY (name);
15
16 CREATE TABLE principalrole (
17 username VARCHAR(30) NOT NULL,
18 groupname VARCHAR(30) NOT NULL
19 );
20
21 ALTER TABLE principalrole ADD CONSTRAINT fk_t_prinicipal FOREIGN KEY (username) REFERENCES principals(name);
22 ALTER TABLE principalrole ADD CONSTRAINT fk_t_roles FOREIGN KEY (groupname) REFERENCES roles(name);
23 ALTER TABLE principalrole ADD CONSTRAINT pk_t_principalrole PRIMARY KEY (username,groupname);

We need some test accounts as well. The following DML-statements will populate the tables with two accounts. The 'supertester' account holds both the 'appadmin' role and the 'appuser' role whereas the 'tester' account holds only the 'appuser' role. The Base64 encoded salt values have been chosen by chance. The password columns contains the password hash value concatenated with the salt as explained in section SaltedPasswordLoginModule.

1 INSERT INTO principals (name, password, salt, disabled, counter) VALUES ('supertester', 'iSX8nhg/eOHGBaF5SDl53cBvOTfYXf37Kktiwt4DMQo=', '6w462o3/75NJ/6ssuckopDskCOU0MUt3zAL6EfOjS7CME/P3tHoHEtVRrtisurfW2tZ6t1w+n7DT0P+t5vH92gThwKNQnPsZfc/wjJtefSyvoR5xnHI50F0JsjrxFrFA', 'N', 1);
2 INSERT INTO principals (name, password, salt, disabled, counter) VALUES ('tester', '8PlYV724EZJwTdJ4ViBrkMq7gxyxJAWTKqy93/AQ/8Y=', '2wZ/PVyRJJm3KkxUpmD5A2GnwNB51ZHuRCp7pZ6aXwwqZtlKITXXqbKDJaic+u5zPuSZEo+nHBgpH+X+p8NjEZVn7WDxajPTCdkK4jq+JQW4k2seOfq0cy6lGT8zVBU9', 'N', 1);
3
4 INSERT INTO roles (name, description) VALUES ('appadmin', 'Grants administration access to the web application.');
5 INSERT INTO roles (name, description) VALUES ('appuser', 'Indicates a normal webapp user with no administration access.');
6
7 INSERT INTO principalrole (username, groupname) VALUES ('supertester', 'appadmin');
8 INSERT INTO principalrole (username, groupname) VALUES ('supertester', 'appuser');
9 INSERT INTO principalrole (username, groupname) VALUES ('tester', 'appuser');

The plaintext passwords of 'supertester' and 'tester' are 'Abracadabra' or rather 'Simsalabim'.

[Top]

Configuration - JDBC driver and Datasource

The application server needs access to the database containing the credentials. Thus we need to install a JDBC driver compatible to the used database. Download the driver from your database vendor. This guide assummes a JDBC 4 compliant driver. There are several options to install a JDBC driver: via Admin GUI, as module or by deploying. We are using the latter method. First start the server:

$ cd <WILDFLY_INSTALL_DIR>/bin
$ ./standalone.sh --server-config=standalone-full.xml

Now switch to another shell within the same directory and type:

$ ./jboss-cli.sh --connect --commands="deploy <PATH_TO_DRIVER>/mysql-connector-java-5.1.30-bin.jar"

Next we request a list of the deployed drivers:

$ ./jboss-cli.sh --connect --commands=/subsystem=datasources:installed-drivers-list
{
    "outcome" => "success",
    "result" => [
        {
            "driver-name" => "mysql-connector-java-5.1.30-bin.jar_com.mysql.fabric.jdbc.FabricMySQLDriver_5_1",
            ...
        },
        {
            "driver-name" => "h2",
            ...
        },
        {
            "driver-name" => "mysql-connector-java-5.1.30-bin.jar_com.mysql.jdbc.Driver_5_1",
            "deployment-name" => "mysql-connector-java-5.1.30-bin.jar_com.mysql.jdbc.Driver_5_1",
            "driver-module-name" => undefined,
            "module-slot" => undefined,
            "driver-datasource-class-name" => undefined,
            "driver-xa-datasource-class-name" => undefined,
            "driver-class-name" => "com.mysql.jdbc.Driver",
            "driver-major-version" => 5,
            "driver-minor-version" => 1,
            "jdbc-compliant" => false
        }
    ]
}

The last line stating that the driver isn't JDBC-compliant seems somewhat odd, but look at Bug #62038 Connector/J Driver reports itself as not jdbc compliant. So this comes from MySQL not being SQL-92 compliant.

Now we can configure a datasource by referencing the name of the previously installed driver. Additionally, we have to specify the (JNDI-)name of the datasource, a connection URL, user credentials for the application server and the connection pool size:

$ ./jboss-cli.sh --connect --commands="data-source add --name=salt_example --connection-url=jdbc:mysql://localhost:3306/salt_example --jndi-name=java:/jdbc/SaltExampleDS --driver-name=mysql-connector-java-5.1.30-bin.jar_com.mysql.jdbc.Driver_5_1 --user-name=<USER_NAME> --password=<PASSWORD> --max-pool-size=15 --min-pool-size=5"

We are using a (MySQL-)database named 'salt_example' here. The application server needs an account when connecting to the database. Within the Example Application the datasource can be requested by its JNDI name 'java:/jdbc/SaltExampleDS'. You may need to enable the datasource:

$ ./jboss-cli.sh --connect --commands="data-source enable --name=salt_example"

You may test the configured connection via the Admin Console. Before you can do this you need a management account. Consult your Getting Started with WildFly 8 or rather Getting Started with JBoss Application Server 7 if required. Now stop the server again:

$ ./jboss-cli.sh -c --command=shutdown
Wildfly 8.2.0

Or rather:

$ ./jboss-cli.sh -c --command=:shutdown
JBossAS 7.1.1

[Top]

Configuration - SSL setup

The whole procedure with (salted) hashed passwords would be fully pointless if the plaintext passwords were sent over the wire. Hence we need an encrypted connection between the client and the server. The SSL protocol uses public key cryptography to negotiate a symmetric session key for further encryption between both parties. That is we need to create a key pair by using the keytool command provided by the JDK (see Security Tools for further information):

$ cd <WILDFLY_INSTALL_DIR>/standalone/configuration
$ export JAVA_HOME=<PATH_TO_JDK>
$ ${JAVA_HOME}/bin/keytool -genkeypair -alias server -keyalg RSA -keystore server.keystore -validity 365 -storepass changeit -dname "cn=myserver" -keypass changeit

This creates a keystore with password 'changeit' containing our keypair which can be referenced by the alias 'server'. Next we have to fiddle with the standalone-full.xml configuration file.The following XML segment must be inserted within the <security-realms> section if you use WildFly 8.2.0. The procedure is different for JBossAS 7.1.1 and will be explained later on.

1 <server xmlns="urn:jboss:domain:2.2">
2 ...
3 <management>
4 <security-realms>
5 ...
6 <security-realm name="MySecurityRealm">
7 <server-identities>
8 <ssl>
9 <keystore path="server.keystore" relative-to="jboss.server.config.dir" keystore-password="changeit" alias="server"/>
10 </ssl>
11 </server-identities>
12 </security-realm>
13 ...
14 </security-realms>
15 ...
16 </management>
17 ...
18 </server>
Wildfly 8.2.0

Now we must define a https listener within the undertow-subsystem which uses the just now configured security realm.

1 <server xmlns="urn:jboss:domain:2.2">
2 ...
3 <profile>
4 ...
5 <subsystem xmlns="urn:jboss:domain:undertow:1.1">
6 ...
7 <server name="default-server">
8 ...
9 <https-listener name="ssl-listener-1" socket-binding="https" security-realm="MySecurityRealm"/>
10 ...
11 </server>
12 ...
13 </subsystem>
14 ...
15 </profile>
16 ...
17 </server>
Wildfly 8.2.0

For JBossAS 7.1.1 you would have to insert the following XML segment. First search for the definition of the normal http connector within the web-subsystem (namespace is urn:jboss:domain:web:1.1). There must be a redirect-port-attribut supplemented. This ensures that http requests will be redirected to the secured connection.

1 <server xmlns="urn:jboss:domain:1.2">
2 ...
3 <profile>
4 ...
5 <subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false">
6 <connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http" redirect-port="8443"/>
7 <connector name="ssl-listener-1" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true">
8 <ssl key-alias="server" password="changeit" certificate-key-file="${jboss.server.config.dir}/server.keystore"/>
9 </connector>
10 ...
11 </subsystem>
12 ...
13 </profile>
14 ...
15 </server>
JBossAS 7.1.1

[Top]

Configuration - Security Domain

What is missing is the connection between our SaltedPasswordLoginModule and the application. The first step in this direction is to define a security-domain for authentication within standalone-full.xml which references the fully qualified class name of our login module. The final link will be the definition of the web descriptors at application level as explained in the next section.

1 <server xmlns="urn:jboss:domain:2.2">
2 ...
3 <profile>
4 ...
5 <subsystem xmlns="urn:jboss:domain:security:1.2">
6 <security-domains>
7 ...
8 <security-domain name="MyFormAuthentication" cache-type="default">
9 <authentication>
10 <login-module code="de.christofreichardt.jboss.login.SaltedPasswordLoginModule" flag="required">
11 <module-option name="dsJndiName" value="java:/jdbc/SaltExampleDS"/>
12 <module-option name="principalsQuery" value="SELECT password, salt FROM principals WHERE BINARY name = ? AND disabled = 'N'"/>
13 </login-module>
14 </authentication>
15 </security-domain>
16 ...
17 </security-domains>
18 </subsystem>
19 ...
20 </profile>
21 ...
22 </server>

The configuration above includes the usage of two of the supported basic options, in this case the to be used datasource when retrieving the hashed password and salt and to enforce case sensitivity in string search by MySQL.

[Top]

Configuration - Web descriptors

Within the jboss-web.xml-descriptor in the WEB-INF folder we define the security domain that should be used when a request to a secured resource has been made:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <jboss-web>
3 <security-domain>MyFormAuthentication</security-domain>
4 </jboss-web>

Finally we are ready to secure our web resources by declaration. This will be done within the standard web.xml-descriptor. My Example application uses an unsecured page containing a link to a secured resource. Without a session following the link triggers the display of a predefined login page. Upon successfull login the secured resource can be accessed by users which hold the 'appadmin' or the 'appuser' role. Additionally there exists another page which can be a accessed only by users holding the 'appadmin' role. Below the complete web.xml of the Example application is shown. I'm using a Java Server Faces application here. The interesting things begin with line 20.

1 <?xml version="1.0" encoding="UTF-8"?>
2 <web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
3 <servlet>
4 <servlet-name>Faces Servlet</servlet-name>
5 <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
6 <load-on-startup>1</load-on-startup>
7 </servlet>
8 <servlet-mapping>
9 <servlet-name>Faces Servlet</servlet-name>
10 <url-pattern>*.jsf</url-pattern>
11 </servlet-mapping>
12 <session-config>
13 <session-timeout>
14 30
15 </session-timeout>
16 </session-config>
17 <welcome-file-list>
18 <welcome-file>test.jsf</welcome-file>
19 </welcome-file-list>
20 <security-constraint>
21 <display-name>mySecurityConstraint</display-name>
22 <web-resource-collection>
23 <web-resource-name>application</web-resource-name>
24 <description/>
25 <url-pattern>/pages/main.jsf</url-pattern>
26 </web-resource-collection>
27 <auth-constraint>
28 <description/>
29 <role-name>appadmin</role-name>
30 <role-name>appuser</role-name>
31 </auth-constraint>
32 <user-data-constraint>
33 <description/>
34 <transport-guarantee>CONFIDENTIAL</transport-guarantee>
35 </user-data-constraint>
36 </security-constraint>
37 <security-constraint>
38 <display-name>myAdminSecurityConstraint</display-name>
39 <web-resource-collection>
40 <web-resource-name>management</web-resource-name>
41 <description/>
42 <url-pattern>/pages/admin/manager.jsf</url-pattern>
43 </web-resource-collection>
44 <auth-constraint>
45 <description/>
46 <role-name>appadmin</role-name>
47 </auth-constraint>
48 <user-data-constraint>
49 <description/>
50 <transport-guarantee>CONFIDENTIAL</transport-guarantee>
51 </user-data-constraint>
52 </security-constraint>
53 <login-config>
54 <auth-method>FORM</auth-method>
55 <realm-name>myJDBCRealm</realm-name>
56 <form-login-config>
57 <form-login-page>/login.html</form-login-page>
58 <form-error-page>/error.html</form-error-page>
59 </form-login-config>
60 </login-config>
61 <security-role>
62 <description/>
63 <role-name>appadmin</role-name>
64 </security-role>
65 <security-role>
66 <description/>
67 <role-name>appuser</role-name>
68 </security-role>
69 </web-app>
Wildfly 8.2.0

From line 20 to line 36 the first security constraint is defined. This constraint secures the resource /pages/main.jsf and covers the authentication and the encryption of the transported user data. That is any user who wants to access the resource must be authenticated and all the data will be encrypted during the process. The authentication succeeds if a certain user holds the 'appadmin' or 'appuser' role. The second security constraint (line 37 to 52) secures the resource /pages/admin/manager.jsf. Only users with 'appadmin' role will be granted access to the page. Within line 53 to line 60 the authentication method is defined. We are using FORM authentication here, that is we have to provide a custom login.html page which follows some conventions. After a failed authentication the user will be redirected to an error page. From line 61 to 68 the roles known to the declarative security approach are defined.

The web.xml-descriptor for JBossAS 7.1.1 is almost identical. Only the namespace, version and schemalocation declared in line 2 are different:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
... ...
69 </web-app>
JBossAS 7.1.1

[Top]

Valid XHTML 1.0 Strict