Chapter 11. Securing JBoss AS 7 Applications

In the previous chapter we have described how to deploy your application in a robust and reliable environment using clustering. The last stop in our journey will be learning about security, which is a key element of any Enterprise application. You must be able to control and restrict who is permitted to access your applications and what operations users may perform.

The Java Enterprise Edition (Java EE) specification defines a simple role-based security model for Enterprise JavaBeans (EJBs) and web components. The implementation of JBoss security is delivered by the Picketbox framework (formerly known as JBoss security), which is part of the application server and provides the authentication, authorization, auditing, and mapping capabilities to Java applications.

Here is the specific list of topics we will cover:

  • A short introduction to the Java security API
  • The foundation of the JBoss AS 7 security subsystem
  • Defining and applying login modules for securing Java EE applications
  • Using the Secure Sockets Layer (SSL) to encrypt the transport layer

Approaching the Java security API

Java EE security services provide a robust and easily configurable security mechanism for authenticating users and authorizing access to application functions and associated data. To better understand the topics related to security, we should at first give some basic definitions:

  • Authentication: It is the process by which you can verify who is currently executing an application, regardless of whether it is an EJB or a servlet (and so on). Authentication is usually performed by means of a Login module contained in a web/standalone application.
  • Authorization: It is the process by which you can verify if a user has the right (permission) to access system resources. Authorization, therefore, presupposes that authentication has occurred; it would be impossible to grant any access control if you don't know who the user is first. The difference between authentication and authorization is depicted by the following diagram:
    Approaching the Java security API

In Java EE, the component containers are responsible for providing application security. A container basically provides two types of security: declarative and programmatic. Let's see them both:

  • Declarative security: It expresses an application component's security requirements by means of deployment descriptors. Because deployment descriptor information is contained in an external file, it can be changed without the need to modify the source code.

    For example, Enterprise JavaBeans components use an EJB deployment descriptor that must be named ejb-jar.xml and placed in the META-INF folder of the EJB JAR file.

    Web components use a web application deployment descriptor named web.xml located in the WEB-INF directory.

    Tip

    Since Java EE 5, you can apply declarative security also by means of annotations just like we have for other key APIs (EJB, web services, and so on). Annotations are specified within a class file and, when the application is deployed, this information is translated internally by the application server.

  • Programmatic security: It is embedded in an application and is used to make security decisions. It can be used when declarative security alone is not sufficient to express the security model of an application. The Java EE security API allows the developer to test whether or not the current user has access to a specific role, using the following calls:
    • isUserInRole() for servlets and JSPs (adopted in javax.servlet.http.HttpServletRequest)
    • isCallerInRole() for EJBs (adopted in javax.ejb.SessionContext)

    Additionally, there are other API calls that provide access to the user's identity:

    • getUserPrincipal() for servlets and JSPs (adopted in javax.servlet.http.HttpServletRequest)
    • getCallerPrincipal() for EJBs (adopted in javax.ejb.SessionContext)

    Using these APIs, you can develop arbitrarily complex authorization models.

JBoss AS 7 security subsystem

JBoss security is qualified as an extension to the application server and it is included by default both in standalone servers and in domain servers:

<extension module="org.jboss.as.security"/>

The following is an extract from the default security subsystem contained in the server configuration file, which contains the RealmUsersRoles login module that will be used in the next section to secure the Ticket example application:

<subsystem xmlns="urn:jboss:domain:security:1.1">
  <security-domains>
      <security-domain name="other" cache-type="default">
          <authentication>
              <login-module code="Remoting" flag="optional">
                  <module-option name="password-stacking" value="useFirstPass"/>
              </login-module>
              <login-module code="RealmUsersRoles" flag="required">
                  <module-option name="usersProperties" value="${jboss.server.config.dir}/application-users.properties"/>
                  <module-option name="rolesProperties" value="${jboss.server.config.dir}/application-roles.properties"/>
                  <module-option name="realm" value="ApplicationRealm"/>
                  <module-option name="password-stacking" value="useFirstPass"/>
              </login-module>
          </authentication>
      </security-domain>
. . . .
  </security-domains>
</subsystem>

As you can see, the configuration is pretty short as it relies largely on default values, especially for high-level structures like the security management area. By defining your own security management options, you could for example, override the default authentication/authorization managers with your implementations. Since it is likely that you will not need to override these interfaces, we will rather concentrate on the security-domain element, which is the core aspect of JBoss security.

A security domain can be thought of as a Customs Office for foreigners. Before the request crosses JBoss AS borders, the security domain performs all the required authorization and authentication checks and eventually notifies if he/she can proceed or not.

Security domains are generally configured at server startup and subsequently bound into the JNDI tree under the key java:/jaas/. Within the security domain, you can configure login authentication modules so that you can easily change your authentication provider by simply changing its login-module.

There are several login modules implementations available out of the box; there is obviously not enough room here to describe in detail the features of each module, though we will offer a comprehensive description of some popular options, such as:

  • The RealmUsersRoles login module, which can be used for basic file-based authentication
  • The Database login module, which checks user credentials against a relational database

Note

Should you need further information about login modules, check out the JBoss AS 7.1 documentation at https://docs.jboss.org/author/display/AS71/Security+subsystem+configuration.

Setting up your first login module

In the following section, we will demonstrate how to secure an application using the RealmUsersRoles security domain, which has been introduced earlier. The RealmUserRoles login module is based on the following two files

  • application-users.properties: It contains the list of usernames and passwords
  • application-roles.properties: It contains the mapping between the users and the roles

These files are located in the application server configuration folder and they are updated each time you add a new user via the add-user.sh/add-user.cmd script. For our purposes, we will create a new application user named demouser that belongs to the role Manager, as shown in the following screenshot:

Setting up your first login module

Once the user is added, the application-users.properties file will contain the username and the MD5 encoding of the password:

demouser=290dfdb724ee4ed466b401a22040efd2

Conversely, the application-roles.properties file will contain the roles granted to the demouser username once logged in:

demouser=Manager 

Using the login module in the Ticket web application

We can now apply the RealmUserRoles login module into any Ticket web application described in the book. We will show at first how to provide a BASIC web authentication and then we will show a slightly more complex example using FORM-based authentication.

Note

BASIC-access authentication is the simplest way to provide a username and password when making a request through a browser.

It works by sending an encoded string containing the user credentials. This Base64-encoded string is transmitted and decoded by the receiver, resulting in the colon-separated username and password strings.

Turning on web authentication requires the security-constraints element to be defined in the web application configuration file (web.xml), as shown in the following code snippet:

<web-app>
. . . . . .
<security-constraint>
     <web-resource-collection>
       <web-resource-name>HtmlAuth</web-resource-name>
       <description>application security constraints
       </description>
       <url-pattern>/*</url-pattern>
       <http-method>GET</http-method>
       <http-method>POST</http-method>
     </web-resource-collection>
     <auth-constraint>
       <role-name>Manager</role-name>
     </auth-constraint>
   </security-constraint>
   <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>file</realm-name>
   </login-config>

   <security-role>
      <role-name>Manager</role-name>
   </security-role>
</web-app>

This configuration will add a security constraint on any JSP/servlet of the web application that will restrict access to users authenticated with the role Manager. All login modules shown in the earlier section define this role, so you can just use the login module that suits your needs best.

The next configuration tweak needs to be performed on the JBoss web deployment's descriptor, WEB-INF/jboss-web.xml. You need to declare the security domain here, which will be used to authenticate the users. Since we are using RealmUsersRoles, which is part of the other built-in login module, we will need to include the java:/jaas/other context information:

<jboss-web> 
      <security-domain>java:/jaas/other</security-domain>
</jboss-web>

The following diagram illustrates the whole configuration sequence applied to a Database login module:

Using the login module in the Ticket web application

Once you have deployed your application, the outcome should be a blocking pop-up requesting user authentication, as shown in the following screenshot:

Using the login module in the Ticket web application

Logging in with demouser and the valid password will grant access to the application with the Manager role.

Switching to FORM-based security

FORM-based authentication lets developers customize the authentication user interface, adapting it, for example, to your company's standards. Configuring it in your application requires you to basically modify just the login-config stanza of the security section of your web.xml file. Within it, we will be defining a login landing page (login.jsf) and an error page (error.jsf), in case the login fails. Here is the code snippet for it:

<login-config>
    <auth-method>FORM</auth-method>
        <realm-name>file</realm-name>
        <form-login-config>
            <form-login-page>/login.jsf</form-login-page>
            <form-error-page>/error.jsf</form-error-page>
        </form-login-config>
</login-config>

The login form must contain fields for entering a username and password. These fields must be named j_username and j_password respectively. The authentication form should post these values to the j_security_check logical name. Here's a simple login.jsf page that can be used for this purpose:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"xmlns:h="http://java.sun.com/jsf/html">
  <h1>
    Please Login
  </h1>
  <body>
  <form method="post" action="j_security_check" name="loginForm">
    <h:panelGrid columns="2">
    <h:outputLabel id="userNameLabel" for="j_username" value="Username:"/>
    <h:inputText id="j_username" />
    <h:outputLabel id="passwordLabel" for="j_password" value="Password:"/>
    <h:inputSecret id="j_password" />
     <h:panelGroup>
    <h:commandButton type="submit" value="Login"/>
    </h:panelGroup>
    </h:panelGrid>
  </form>
  </body>
</html>

For the sake of brevity, we won't include the error page, which will simply alert that the user entered an incorrect combination of username and password. The expected outcome is the following login screen, which will intercept all user access to your application and grant access to the default home page if the username and password credentials are correct.

Switching to FORM-based security

Creating a Database login module

The UserRoles login module is a good starting point for learning how to put together all the pieces required for securing a web application. In real-world cases, there are better alternatives to protect your applications, such as the Database login module. A database security domain follows the same logic exposed in the earlier example; it just stores the credentials within the database. In order to run this example, we will refer to a data source defined in Chapter 5, Combining Persistence with CDI (bound at the JNDI name java:jboss/datasources/jbossas7development), which needs to be deployed on the application server:

<security-domain name="mysqldomain" cache-type="default">
  <authentication>
    <login-module code="Database" flag="required">
      <module-option name="dsJndiName" value=" java:jboss/datasources/jbossas7development"/>
      <module-option name="principalsQuery" value="select passwd from USERS where login=?"/>
      <module-option name="rolesQuery" value="select role, 'Roles' from USER_ROLES where login=?"/>
    </login-module>
  </authentication>
</security-domain>

In order to get working with this configuration, you have to first create the required tables and insert some sample data in it:

CREATE TABLE USERS(login VARCHAR(64) PRIMARY KEY, passwd VARCHAR(64));
CREATE TABLE USER_ROLES(login VARCHAR(64), role VARCHAR(32));
INSERT into USERS values('admin', 'admin'),
INSERT into USER_ROLES values('admin', 'Manager'),

As you can see, the admin user will map again to the Manager role. One caveat of this configuration is that it uses clear-text passwords in the database; so before rolling this module into production, you should consider adding additional security to your login module. Let's see how in the next section.

Encrypting passwords

Storing passwords in the database as a clear-text string is not considered a good practice; as a matter of fact, a database has even more potential security holes than a regular file system. Imagine, for example, that a DBA added a public synonym for some tables, forgetting that one of those tables was holding sensitive information like application passwords! You then need to be sure that no potential attackers will ever be able to deliver the following query:

Encrypting passwords

Fortunately, securing application passwords is relatively easy; you can add a few extra options to your Login Module, specifying that the stored passwords are encrypted using a message digest algorithm. For example, in the mysqlLogin module, you should add the following highlighted options at the bottom:

<login-module code="Database" flag="required">
     <module-option name="dsJndiName" value="java:jboss/datasources/jbossas7development"/>
     <module-option name="principalsQuery" value="select passwd from USERS where login=?"/>
     <module-option name="rolesQuery" value="select role, 'Roles' from USER_ROLES where login=?"/>
     <module-option name="hashAlgorithm" value="MD5"/>
     <module-option name="hashEncoding" value="BASE64"/>
     <module-option name="hashStorePassword" value="true"/>
</login-module> 

Here we have specified that the password will be hashed against an MD5 hash algorithm; you can alternatively use any other algorithm allowed by your JCA provider, such as SHA.

Note

For an excellent introduction to hashing algorithms, refer to the following link:

http://www.unixwiz.net/techtips/iguide-crypto-hashes.html

For the sake of completeness, we include here a small application, which uses the java.security.MessageDigest and org.jboss.security.Base64Utils class (contained in the picketbox-4.0.7.Final.jar file that is part of the JBoss AS 7 modules), to generate the Base-64 hashed password that is to be inserted in Database:

public class Hash {

    public static void main(String[] args) throws Exception{
       String password = args[0];
       MessageDigest md = null;
       md = MessageDigest.getInstance("MD5");
       byte[] passwordBytes = password.getBytes();
       byte[] hash = md.digest(passwordBytes);
       String passwordHash =      org.jboss.security.Base64Utils.tob64(hash);
       System.out.println("password hash: "+passwordHash);
}

Running the main program with admin as the argument will generate the hash X8oyfUbUbfqE9IWvAW1/3. This hash will be your updated password, which needs to be updated in your database as shown in the following screenshot.

UPDATE USERS SET PASSWD = 'X8oyfUbUbfqE9IWvAW1/3' WHERE LOGIN = 'admin';

You can update it from any SQL client of your liking.

Encrypting passwords

Using the Database login module in your application

Once you are done with the login module configuration, don't forget to reference it through the JBoss web deployment's descriptor, WEB-INF/jboss-web.xml.

<jboss-web> 
      <security-domain>java:/jaas/mysqldomain</security-domain>
</jboss-web>

Securing EJBs

Securing applications by means of a web login form is the most frequently used option in Enterprise applications. Nevertheless, the HTTP protocol is not the only choice available to access applications. For example, EJBs can be accessed by remote clients using the RMI-IIOP protocol. In such a case, you should further refine your security policies by restricting access to the EJB components, which are usually involved in the business layer of your applications.

Tip

How does security work at the EJB level?

Authentication must be performed before any EJB method is called. Authorization, on the other hand, occurs at the beginning of each EJB method call.

One vast area of improvement introduced in Java EE 5 concerns the use of annotations, which can also be used to perform the basic security checks. There are five available annotations, as follows:

  • @org.jboss.ejb3.annotation.SecurityDomain: This specifies the security domain that is associated with the class/method.
  • @javax.annotation.security.RolesAllowed: This specifies the list of roles permitted to access a method(s) in an EJB application.
  • @javax.annotation.security.RunAs: This assigns a role dynamically to the EJB application during the invocation of the method. It can be used, for example, if we need to temporarily allow a permission to access certain methods.
  • @javax.annotation.security.PermitAll: This specifies that an EJB application can be invoked by any client. The purpose of this annotation is to widen security access to some methods in situations where you don't exactly know what role will access the EJB application (imagine that some modules have been developed by a third party and they access your EJB application with some roles that are not well-identified).
  • @javax.annotation.security.DenyAll: This specifies that an EJB application cannot be invoked by external clients. It has the same considerations as those for @PermitAll.

Here is an example of how to secure the TheatreBookerBean SFSB, which we discussed in Chapter 4, Learning Context Dependency Injection:

@RolesAllowed("Manager")
@SecurityDomain("mysqldomain")
@Stateful
@Remote(TheatreBooker.class) 

public class TheatreBookerBean implements TheatreBooker {

}

Note

Be careful! There is a more than one SecurityDomain API available. You have to include org.jboss.ejb3.annotation.SecurityDomain. The @RolesAllowed annotation, on the other hand, needs importing of javax.annotation.security.RolesAllowed.

Annotations can also be applied at the method level; for example, if we want to secure just the bookSeat object of the TheatreBookerBean class, we would tag the method as follows:

@RolesAllowed("Manager")
@SecurityDomain("mysqldomain")
public String bookSeat(int seatId)  throws SeatBookedException {
}

What about if you don't want to use annotations for establishing security roles? For example, if you have a security role that is used crosswise by all your EJB applications, perhaps it is simpler to use a plain old XML configuration instead of tagging all EJBs with annotations. In this scenario, you have to declare the security constraints first in the generic META-INF/ejb-jar.xml file:

<method-permission>    
  <role-name>Manager</role-name>    
  <method>    
   <ejb-name>*</ejb-name>    
    <method-name>*</method-name>    
  </method>    
</method-permission>

Then, inside the META-INF/jboss-ejb3.xml configuration file, just add a reference to your security domain:

<jboss:ejb-jar>
  <assembly-descriptor>
    <s:security>
      <ejb-name>*</ejb-name>
      <s:security-domain>mysqldomain</s:security-domain>
    </s:security>
  </assembly-descriptor>

</jboss:ejb-jar>

Here's a snapshot illustrating the EJB-file-based role configuration:

Securing EJBs

Securing web services

Web service authorization can basically be carried out in two ways, depending on if we are dealing with a POJO-based web service or an EJB-based web service.

Security changes to POJO web services are identical to those we have introduced for servlets/JSP: consistent in defining the security-constraints element in web.xml and the login modules in jboss-web.xml.

If you are using a web client to access your web service, it is all you need to get authenticated. If you are using a standalone client, you will need in turn to specify the credentials to the JAX-WS Factory. Here is an example of accessing the secured POJOWebService instance, which was described in Chapter 8, Adding Web Services to Your Applications:

  JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

  factory.getInInterceptors().add(new LoggingInInterceptor());
  factory.getOutInterceptors().add(new LoggingOutInterceptor());

  factory.setServiceClass(POJOWebService.class);
  factory.setAddress("http://localhost:8080/pojoService");
  factory.setUsername("admin");
  factory.setPassword("admin");
  POJOWebService client = (POJOWebService) factory.create();
 
  client.doSomething();

What about EJB-based web services? The configuration is slightly different; since the security domain is not specified into web descriptors, we have to provide it by means of annotations:

@Stateless
@WebService(targetNamespace = "http://www.packtpub.com/", serviceName = "TicketWebService") 
@WebContext(authMethod = "BASIC",
            secureWSDLAccess = false)
@SecurityDomain(value = "mysqldomain")

public class TicketSOAPService implements TicketSOAPServiceItf, Serializable {

   . . . . 
}

As you can see, the @org.jboss.ws.api.annotation.Webcontext annotation basically reflects the same configuration options as that of POJO-based web services, with BASIC authentication and unrestricted WSDL access.

The @org.jboss.ejb3.annotation.SecurityDomain annotation should be familiar to you since we have introduced it to illustrate how to secure an EJB. As you can see, it's a replacement for the information contained in the jboss-web.xml file, except that the security domain is referenced directly by mysqldomain (instead of java:/jaas/mysqldomain).

Note

The previous security configuration can also be specified by means of the META-INF/ejb-jar.xml and META-INF/jboss-ejb3.xml files in case you prefer using standard configuration files.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.30.62