Securing Web Applications

Using JBoss allows you to add security entirely through configuration, I have already touched on security in a previous topic which discussed security domains and login modules, now I am going to discuss how to use this within the web application.

By default web applications are not secured, this means that if you write an application and deploy it anyone can access any of the URL's, also by default all connections are not encrypted.

There are a number of files that are used with security

Configuration File
The configuration security aspects
WEB-INF/web.xml
  • The authentication strategy (BASIC, FORM, DIGEST or CLIENT-CERT)
  • Which URL patterns should be restricted to which logical roles
  • A set of logical roles
WEB-INF/jboss-web.xml
  • The JNDI name for the security domain that the web application should use
server/xxx/deploy/jbossweb.sar/server.xml
  • The secure HTTP connector (SSL port, Keystore file and so on)
  • The portion of the certificate compared during CLIENT-CERT authentication
server/xxx/conf/login-config.xml
  • The definition for security domains
  • The login module that the security domain should be

Security in web.xml

The standard web deployment descriptor web.xml is used to specify application-level authentication, authorization and encryption. The picture below displays the general structure of the security elements in the web.xml file, for those of you with a keen eye it looks alot like Tomcats security setup.

I present a complete solution below, everything is self explaining but the transport-guarantee element specifies if the requests coming into the application must be encrypted or not. The web.xml is used to define what URL's are to be secured but says nothing about how to secure them, that's the job of the security domain which is configured in the jboss-web.xml file. The security domain is setup in the login-config.xml file, there are plenty of examples in there for you to adapt.

WEB-INF/web.xml security example

<web-app>
  <security-constraint>

    <web-resource-collection>
      <web-resource-name>Some Resource</web-resource-name>
      <url-pattern>/*</url-pattern>
      <url-pattern>/shoppingcart/*</url-pattern>
      <http-method>GET</http-method>
      <http-method>POST</http-method>
    </web-resource-collection>

    <auth-constraint>
      <role-name>SomeSimpleRole</role-name>
    </auth-constraint>

    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>

  </security-constraint>

  <login-config>
    <auth-method>BASIC</auth-method>
  </login-config>

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

jboss-web.xml <jboss-web>
  <security-domain>java:/jaas/some-domain</security-domain>
</jboss-web>

Note: we use the JNDI name which always starts with java:/jaas, all this does is to map the application to the security domain located in the login-config.xml file
login-config.xml <application-policy name="some-domain">
  ...
</application-policy>

To setup the encrypted communications you need to create a keystore which I discussed in securing applications, you use the certificate within the keystore for the encryption.

Enable secure HTTP connector <Connector protocol="HTTP/1.1" SSLEnabled="true"
           port="8443" address="${jboss.bind.address}"
           schema="https" secure="true" clientAuth="false"
           keystoreFile="${jboss.server.home.dir}/conf/keystore.pfv"
           keystorePass="rmi+ssl" sslProtocol="TLS"
/>

To integrate the JBoss Web Server into the JBoss SX security framework you need to uncomment the below in the server.xml file

integrate the JBoss Web Server into the JBoss SX <Realm className="org.jboss.web.tomcat.security.JbossWebRealm"
       certificatePrincipal="org.jboss.security.auth.certs.SubjectDNMapping"
       allRolesMode="authonly"
/>

So what happens when a user requires a secure page

Authentication Strategies

There are a number of ways to authenticate a user, prompting a user for a login and a password is the most common, Java EE defines two main password authentication strategies that can be used by web applications HTTP basic authentication and form-based authentication. A third strategy called digest authentication is available in JBoss, you can also use client-certificate authentication.

The deployment descriptor WEB-INF/web.xml defines authentication using the login-conf element

defining authentication <login-config>
  <auth-method>BASIC</auth-method>
</login-config>

The authentication strategies that are available are the following

Authentication Strategy
<auth-method> value
Description
HTTP basic
BASIC
Causes the browser to pop up a modal dialog box prompting the user for their password
Form-Based
FORM
An HTML page with a login form is sent to the browser for the user to login with
HTTPS client
CLIENT-CERT
If the client has a public-key certificate, the server verifies the certificate
Digest
DIGEST
Causes the browser to present a dialog box, but the password is hashed in a digest that includes other information before it is sent to the server.

A web application can only have a single <login-config> defined which means yo can have only one authentication method for an application.

Here are some examples to get you going

HTTP basic # WEB-INF/web.xml

<login-config>
  <auth-method>BASIC</auth-method>
  <realm-name>My Site</realm-name>
</login-config>

Note: the realm-name element specifies descriptive text that sent back to the client
Form-Based # inside your login HTML page (login.html)

<form name="loginForm" method="post" action="j_security_check">
  <table>
    <tr>
      <td>User Name:</td>
      <td><input type="text" name="j_username"></td>
    </tr>
    <tr>
      <td>User Password:</td>
      <td><input type="text" name="j_password"></td>
    </tr>
    <tr colspan="2">
      <td><input type="submit" value="login"></td>
    </tr>
  </table>
</form>

Note: the j_security, j_username and j_password are required

# WEB-INF/web.xml

<login-config>
  <auth-method>FORM</auth-method>
  <form-login-config>
    <form-login-page>/restricted/login.html</form-login-page>
    <form-error-page>/restricted/bad-login.html<form-error-page>
  </form-login-config>
</login-config>
Digest

# WEB-INF/web.xml

<login-config>
  <auth-method>DIGEST</auth-method>
  <realm-name>Default</realm-name>
</login-config>

# server/xxx/conf/login-config.xml

<application-policy name="jmx-console">
  <authentication>
    <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required">
      <module-option name="usersProperties">props/jmx-console-users.properties</module-option>
      <module-option name="rolesProperties">props/jmx-console-roles.properties</module-option>
      <module-option name="hashAlgorithm">MD5</module-option>
      <module-option name=hashEncoding">rfc2617</module-option>
      <module-option name=hashUserPassword>false</module-option>
      <module-option name="hashStorePassword>true</module-option>
      <module-option name="passwordIsA1Hash>true</module-option>
      <module-option name="storeDigestCallback">org.jboss.security.auth.spi.RFC2617Digest</module-option>
    </login-module>
  </authentication>
</application-policy>

# if you need to obtained a hashed version of your password you can use the following, the output is a hashed password, you need to download the jbosssx.jar file first

java -cp jbosssx-server.jar org.jboss.security.auth.spi.RFC2617Digest username "realm" password

Authorizing Users

Although the user may have authenticated this does no mean that he/she has access to a web a page or resource, authorization is configured via the WEB-INF/web.xml file by associating role names with URL patterns, again this is very similar to Tomcat.

authorization example

<security-constraint>
  ...
  <auth-constraint>
    <role-name>SomeSimpleRole</role-name>
    <role-name>SomeOtherRole</role-name>
  </auth-constraint>
</security-contsraint>

<security-role>
  <role-name>SomeSimpleRole</role-name>
</security-role>
<security-role>
  <role-name>SomeOtherRole</role-name>
</security-role>

Note: only the security-constraint is required, the security-role is optional. You could use a asterisk (*) in the role-name to allow anyone in but see below for more information.

The behavior of a role can be controlled in the realm definition in the JBoss Web Server server/xxx/deploy/jbossweb.sar/server.xml

controlling role-name <Realm className="org.jboss.web.tomcat.security.JBossWebRealm"
       certificatePrincipal="org.jboss.security.auth.certs.SubjectDNMapping"
       allRolesMode="authOnly"
/>

The allRolesMode above determines the behavior when a web application defines an auth-constraint block with a role-name equal to * in the web.xml file, there are three options that you can use

allRolesMode option Description
strict This option interprets the servlet specification strictly by requiring that the user be in a role defined by a role-name element in the web.xml file
authOnly Allows any authenticated user

strictAuthOnly

Because the security-role definition is optional, you can use this setting to get strict behavior when security-role elements are defined and authOnly behavior when none is defined

Encrypting Web Comms

Web applications can enable SSL to prevent eavesdropping and to assure users that the application are hosted by the correct site. To handle HTTPS requests you must do the following:

You can create a self-signed certificate to use for testing purposes

create self-signed cert keytool -genkey -alias serverCert -keyalg RSA -validity 365 -keystore keystore.pfv

Now edit the server.xml file so that the HTTPS connector points to the keystore, everything below is pretty self-explaining

server.xml

<Connector port="8443"
  ...
  scheme="https"
  secure="true"
  clientAuth="false"
  keystoreFile="${jboss.server.home.dir}/conf/server.keystore"
  keystorePass="password"
  sslProtocol="TLS"
/>

Note: there is another example above

Sometimes you may want the user to use HTTPS only on a particular web application, to do this you use the mechanism transport guarantee it is defined in the security-constraint element in your application deployment descriptor file WEB-INF/web.xml. When the user requests a page on the HTTP connector that has a security-constraint, the request is then redirected to the port specified by redirectPort.

force HTTPS use # WEB-INF/web.xml

<security-constraint>
  ...
  <user-data-constraint>
    <transport-guarantee>CONFIDENTIAL</transport-guarantee>
  </user-data-constraint>
</security-constraint>

# server.xml

<Connector protocol="HTTP/1.1" port="8080"
           address="${jboss.bind.address}"
           connectionTimeout="20000"
           redirectPort="8443"
/>

There are three <transport-guarantee> options

CONFIDENTIAL application requires that the data be transmitted to prevent other entities from observing the contents of the transmission. You basically get redirected to the secure connector.
INTEGRAL means that data sent between a client and a server can't be changed in transit. You basically get redirected to the secure connector.
NONE means that you are not setting the transport-guarantee at all.

There is one other secured mechanism you can use and that is mutual authentication, this means that the server validates the client, I am going leave this for you to investigate as it is not commonly used.