JBoss Community Archive (Read Only)

JBoss AS 7.2

Hardening Guidelines

Purpose of this Guide

This guide is intended for organizations or individuals that are using JBoss Application Server 7 (in standalone or domain mode) on secure production systems.

Format

The format of the examples is XML, all the code is intended as XML fragments of the full configuration file.

Some examples are valid only for a specific operating mode (standalone mode or domain). For more information on the JBoss AS7 operating modes please consult Operating modes.

Prerequisites

Running instance of JBoss Application Server 7.2. How to get started with JBoss Application Server 7.2 is in the Getting Started Guide

Notes on encryption

Before implementing encryption on your data at any level (transmission, storage), understand if encrypting the data is necessary. Using SSL will cost hardware resources and will generally slow down your application server.

Always estimate how important is it to encrypt your data and exactly what data you need to encrypt.

Standalone Mode

Remove or Disable Example Content

Although it can be a good idea to keep some example content in a test environment, to check if the server is running correctly, it is not a good practice in production.

Removing the samples will prevent an external client seeing what version of the AS you are running and will also prevent the AS instance being affected by future security bugs on this content.

Delete the example datasource shipped with the default configuration:

 <datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
  <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</connection-url>
  <driver>h2</driver>
   <security>
     <user-name>sa</user-name>
     <password>sa</password>
   </security>
</datasource>

Disable or remove the welcome-content directory by placing "false" as a value on enable-welcome-root

 <subsystem xmlns="urn:jboss:domain:web:1.4" default-virtual-server="default-host" native="false">
  <connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http"/>
  <virtual-server name="default-host" enable-welcome-root="false">
        <alias name="localhost"/>
        <alias name="example.com"/>
  </virtual-server>
</subsystem>

You can also change the alias name according to your needs.

You can remove the directory holding the sample content:

In Linux as the JBoss AS user:

rm -r $JBOSS_HOME/welcome-content

Audit Logging

It is good practice to log the actions on the org.jboss.security class. This category includes all the security related actions on the particular instance of the AS (authentication, failures, administrative changes ..).

It is good to keep these events in a separate file for easy classification and analysis. In this example the file will be called audit.log

Insert a new handler in our logging subsystem

<periodic-rotating-file-handler name="AUDIT" autoflush="true">
  <formatter>
      <pattern-formatter pattern="%d{HH:mm:ss,SSS}%-5p %c (%t) %s%E%n"/>
  </formatter>
  <file relative-to="jboss.server.log.dir" path="audit.log"/>
  <suffix value=".yyyy-MM-dd"/>
  <append value="true"/>
</periodic-rotating-file-handler>

Create a new category and reference the handler created above

 <logger category="org.jboss.security">
     <level name="TRACE"/>
     <handlers>
          <handler name="AUDIT"/>
     </handlers>
</logger>

Send log to a remote server

Sending logs to a remote location is an excellent way to increase security (preventing log tampering) and improve your application error analysis. Jboss AS7.2 supports a syslog handler designed for this purpose.

Add the syslog handler in your logging subsystem:

In the section:

<subsystem xmlns="urn:jboss:domain:logging:1.1">

...

</subsystem>

Add this handler:

<syslog-handler name="SYSLOG">
   <level name="INFO"/>
   <hostname value="${jboss.bind.address}"/>
   <app-name value="JBossAS7"/>
    <server-address value="Your Log Server's IP (ie. 10.1.1.1)"/>
</syslog-handler>

For a more detailed explanation of all the parameters in this handler, please consult the Logging Configuration section of the management guide

Note on Exceptions

Even though JBoss supports a Syslog handler out of the box, some exceptions are often discarded by many syslog compatible servers because the exception message spans in multiple lines.

Please consult the documentation of your log server to understand if multi line logs are supported.

Configure granular log level if required

If your application is handling sensitive data or you are regulated by any data security compliance, you may want to reduce the log level of the sensitive classes of your application to avoid logging sensitive data on production system.

If your log file is for some reason compromised, the attacker may reach sensitive data stored in the logs if the class log level is not set up properly.

 <logger category="com.arjuna">
                <level name="WARN"/>
            </logger>
            <logger category="org.apache.tomcat.util.modeler">
                <level name="WARN"/>
            </logger>
            <logger category="sun.rmi">
                <level name="WARN"/>
            </logger>
            <logger category="jacorb">
                <level name="WARN"/>
            </logger>
            <logger category="jacorb.config">
                <level name="ERROR"/>
            </logger>
             ...
            <logger category="very.sensitive.information.here">
                <level name="SEVERE"/> <!-- Use a log level that prints less sensitive information as possible -->
            </logger>

In this way you can specify a granular log level for each class that you wish to secure. Refer to Logging Configuration and Subsystem configuration for more details on how to set up the logging subsystem.

Run your Instance as non privileged user

This is a global requirement for any mission-critical application, your AS 7 instance should never run as root account in a secure environment.

Please refer to your operating system documentation to understand how to run AS7 as non privileged user.

File system permissions of log files

In order to prevent modifications to your log files, you can restrict the OS permissions to only be readable/writable by the JBoss user.

In order to change the location of your log files you can use this Java property at startup jboss.domain.log.dir and set a different log directory (I.E. /var/log/jboss)

-Djboss.domain.log.dir=/var/log/jboss/

Check the documentation of your operating system to understand how to restrict the access to your files.

Datasources

In order to secure your datasources, it is good practice is associate them with a security domain.

This example will implement an Oracle datasource and it will delegate the authentication to a JAAS security domain.

Configure a security domain called "oracle-secure" to use with your datasource:

 <security-domains>
                <security-domain name="oracle-secure" cache-type="default">
                    <authentication>
                     <login-module code="SecureIdentity" flag="required">
                            <module-option name="username" value="myexpensivedbusername"/>
                            <module-option name="password" value="myexpensivedbencryptedpassword"/>
                            <module-option name="managedConnectionFactoryName" value="jboss.jca:service=LocalTxCM,name=MyDS-MyDS"/>
                    </authentication>
                </security-domain>
...
</security-domains>

As you can see, between the <authentication></authentication> tags we have defined a username and a password for our database authentication.

The part:

<login-module code="SecureIdentity" flag="required">

defines which module to use and also to flag it as "required". This means that the client cannot pass the authentication if these credentials are not provided correctly.

The password value can be defined in plain-text or in an encrypted format. If you want to use the encrypted value just use Picketbox and provide the password as input:

java -cp $JBOSS_HOME/modules/org/picketbox/main/picketbox-4.0.6.<beta|final>.jar:$JBOSS_HOME/modules/org/jboss/logging/main/jboss-logging-3.1.0.<some_version>.jar:$CLASSPATH org.picketbox.datasource.security.SecureIdentityLoginModule password
Changing the Key

The result of this operation is an encrypted value, however the key used is hardcoded in the source code, from a security perspective, using a basically public passphrase is exactly the same as non encrypting at all. If you really want to make this secure please change the hardcoded passphrase in Picketbox.

You can possibly change the hardcoded key by modifying the source code of this Class. However, you may need to recompile and test the component yourself.

Using the Vault

Also note this article Securing Passwords on how to use the Vault functionality

Finally you can setup the datasource with your security domain

 <datasource jta="true" jndi-name="java:/MyDS" pool-name="MyDS" enabled="true" use-ccm="true">
                    <connection-url>jdbc:oracle:thin:@myexpensivedatabase:1521:myexpensivedbuser</connection-url>
                    <driver-class>oracle.jdbc.OracleDriver</driver-class>
                    <driver>oracle</driver>
                    <pool>
                        <min-pool-size>5</min-pool-size>
                        <max-pool-size>50</max-pool-size>
                    </pool>
                    <security>
                        <security-domain>oracle-secure</security-domain>
                    </security>
                    <validation>
                        <validate-on-match>true</validate-on-match>
                        <background-validation>false</background-validation>
                        <check-valid-connection-sql>select 1 from dual</check-valid-connection-sql>
                    </validation>
                    <timeout>
                        <idle-timeout-minutes>1</idle-timeout-minutes>
                    </timeout>
                    <statement>
                        <prepared-statement-cache-size>50</prepared-statement-cache-size>
                        <share-prepared-statements>true</share-prepared-statements>
                    </statement>
                </datasource>

Your AS is now able to expose a datasource, connect to a database and delegate the authentication part to a security domain with an encrypted password.

Deployment Scanner

The deployment scanner scans the file system where your AS instance is running to automatically deploy any new application copied in your deploy directory. If it is not needed, the deployment scanner must be disabled in order to prevent unauthorized files being deployed.

Just configure the scan-interval parameter as -1 , this will configure the deployment scanner to only allow deployments from console or at instance startup.

<subsystem xmlns="urn:jboss:domain:deployment-scanner:1.1">
 <deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="-1"/>
</subsystem>

Web Subsystem

Enable SSL Connector

Enabling the SSL connector for the web subsystem will encrypt everything that is using that particular port

 <subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="example.com" native="false">
            <connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true" enable-lookups="false">
                <ssl name="ssl" password="changeit" certificate-key-file="example.ks" verify-client="true" key-alias="example-alias"/>
            </connector>
            <virtual-server name="example.com" enable-welcome-root="false">
              <alias name="www.example.com"/>
            </virtual-server>
               ...
        </subsystem>

For more information on the connector attributes refer to JbossWeb Configuration Reference

Create an entry in the socket binding group for the port of this new connector, if not present

<socket-binding-group name="standard-sockets" default-interface="public"
                          port-offset="${jboss.socket.binding.port-offset:0}">
           ...
        <socket-binding name="https" port="7443"/>
          ...
    </socket-binding-group>
JSP Regeneration

If you do not need automatic regeneration of JSP pages, set up the Web Subsystem to not regenerate the content automatically. This can prevent someone injecting code in your JSP resources and compiling them without your knowledge.

 <connector>
 ...
</connector>
<virtual-server name="example.com" enable-welcome-root="false">
      ...
</virtual-server>
<configuration>
 <jsp-configuration development="false"/>
</configuration>

The "Development" value set to false will prevent JSP resources being automatically generated and force a restart in order to implement the changes.

Remove and mask informational headers

When a resource is requested from the Web Connector via HTTP, the response contains headers with information about the server which generated the response.

HTTP/1.1 200 Found
X-Powered-By: JSF/1.2
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 1100
Date: Thu, 1 Dec 2012 03:27:30 GMT
Server: Apache-Coyote/1.1

This information can help an attacker to quickly identify which version you are running and consequently tune the attack attempts specifically for your software instance.

In order to hide or mask these headers we can set up the web subsystem with the following:

  <subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="example.com" native="false">
            <connector>
            ...
            </connector>
            <virtual-server name="example.com" enable-welcome-root="false">
            ...
            </virtual-server>
            <configuration>
                <jsp-configuration x-powered-by="false"/>
            </configuration>
  </subsystem>
Bug on jsp-configuration

Sometimes the x-powered-by configuration element is not interpreted correctly and the actual property value in tomcat remains set to true.
If this is your case you can override globally this property value with:

<property name="org.apache.catalina.connector.X_POWERED_BY value=false"/>

And at this point the header should disappear completely.

Please note that this setting will be reflected on the actual response headers only if your application is using the JBoss AS 7.2 Web subsystem JSF library. If your application is using a custom library you must check the relevant documentation of your library version on how to disable this header

.

You should mask the "Server:" value in the header. By default this header shows the version of our servlet container.

In order to remove it we can set up a global property in this way:

<system-properties>
 ...
 <property name="org.apache.coyote.http11.Http11Protocol.SERVER" value="SuperSecureServer"/>
 ...
</system-properties>

At this stage, the response headers should look like this:

HTTP/1.1 200 Found
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 1100
Date: Thu, 1 Dec 2012 03:27:30 GMT
Server: SuperSecureServer
Disable stacktrace in response body

Stacktraces in response bodies are useful for quick debugging in development and test environments. However in production, showing stracktraces can leak sensitive information to the client.

To disable this particular functionality, the display-source-fragment directive must be set to false.

<subsystem xmlns="urn:jboss:domain:web:1.1" ... >
   ....
<configuration>
  <jsp-configuration display-source-fragment="false"/>
</configuration>
   ....
</subsystem>

Webservices Subsystem

SSL Encryption

Define the secure WSDL port leveraging on the Web Subsystem ssl connector.

<subsystem xmlns="urn:jboss:domain:webservices:1.1">
           <modify-wsdl-address>true</modify-wsdl-address>
           <wsdl-host>10.1.1.1</wsdl-host>
           <wsdl-secure-port>7443</wsdl-secure-port> <!-- Must be a secure SSL connector port -->
           <endpoint-config name="Standard-Endpoint-Config"/>
           <endpoint-config name="Recording-Endpoint-Config">
               <pre-handler-chain name="recording-handlers"
                                  protocol-bindings="##SOAP11_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP ##SOAP12_HTTP_MTOM">
                   <handler name="RecordingHandler"/>
               </pre-handler-chain>
           </endpoint-config>
 </subsystem>
Please note that <modify-wsdl-address> needs to be set to true in order for the Webservices subsystem to change all the http:// links in your WSDL file to https://, and instruct the client to go only to the secure version of your resources.

Authentication

To create a security domain for your Webservices endpoints, you can use different login modules. Please consult the Security subsystem configuration for more detailed informations.

In the example a Database module is used.

  <security-domains>
                <security-domain name="webservices-auth" cache-type="default">
                    <authentication>
                        <login-module code="Database" flag="required">
                            <module-option name="dsJndiName" value="java:/MyDS"/>
                            <module-option name="principalsQuery"
                                           value="SQL QUERY TO SELECT YOUR USERNAME"/>
                            <module-option name="rolesQuery"
                                           value="SQL QUERY TO SELECT YOUR ROLE"/>
                            <module-option name="hashAlgorithm" value="SHA-256"/>
                            <module-option name="hashEncoding" value="base64"/>
                        </login-module>
                    </authentication>
                </security-domain>
...
</security-domains>
This security domain will query your database and search if the username/password pair provided to your Webservice by the client are correct.

Reference your new security domain in your code

package my.class.ws;

import org.jboss.ejb3.annotation.SecurityDomain;
import org.jboss.ws.api.annotation.WebContext;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Stateless;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

...

@Stateless

@WebService(name = "Member", targetNamespace = "http://my.ws/example", serviceName = "ExampleWS")

@SOAPBinding(style = SOAPBinding.Style.RPC, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)

@SecurityDomain("webservices-auth")

@RolesAllowed("wsrole")

@WebContext(secureWSDLAccess = true, transportGuarantee = "CONFIDENTIAL", authMethod = "BASIC")

...

The annotation SecurityDomain obviously configure the security domain used for authentication purposes.

The annotation RolesAllowed defines which security roles (from your security domain) are allowed to contact this webservice

The annotation transportGuarantee force the SSL/TLS connection

The annotation authMethod defines which method your webservice will use to authenticate the client.

ORB (JacORB) Subsystem

The AS 7 ORB implementation can be secured by enabling SSL encryption in the JacORB subsystem.

First of all, create a security domain that references your keystore/truststore configuration. We'll call it "ssl-domain"

<security-domains>     ....
     <security-domain name="ssl-domain">
        <jsse keystore-password="changeit" keystore-url="mykeystore.ks" truststore-url="mytruststore.ks" truststore-password="changeit" server-alias="my alias"/>
     </security-domain>
     ....
</security-domains>

To enable this in JacORB setup the tags security-domain and security.

 <subsystem xmlns="urn:jboss:domain:jacorb:1.1">
         <orb>
           <initializers security="on" transactions="spec"/>
         </orb>
         <security support-ssl="on" security-domain="ssl-domain"/>
 </subsystem>

This configuration enables SSL on JacORB. If you want to lock-down this subsystem more, you can find a huge amount of useful informations on the JacORB Official Documentation Page

Messaging subsystem (HornetQ)

Setup a security domain

Like the other subsystems, the messaging subsystem can be linked to a security-domain in order to delegate encryption or authentication settings.

Reference the name of your security domain after the <hornetq-server> element

<subsystem xmlns="urn:jboss:domain:messaging:1.1">
            <hornetq-server>
                <security-domain>mysecuritydomain</security-domain>
                ...
            </hornetq-server>
</subsystem>

As seen before, the security domain can set up the parameters for encryption with SSL or authentication.

Role Based Authentication per queue

In order to have  role based authentication inside our queue server you need to setup HornetQ with these directives.

you can restrict the access to particular queue (read/write) or even prevent the creation, deletion of new queues, with a basic role based access control.

<security-settings>
                    <security-setting match="#">
                        <permission type="send" roles="MyRole"/>
                        <permission type="consume" roles="MyRole"/>
                        <permission type="createNonDurableQueue" roles="MyRole"/>
                        <permission type="deleteNonDurableQueue" roles="MyRole"/>
                    </security-setting>
</security-settings>
The match element tells to hornetq which queue address is interested in this authentication setting. To better understand how to setup this parameter, refer to the official HornetQ documentation.
Once the queue name is matched, your client has to be in the defined role in order to perform the desired operation. Usually the best practice is to leverage the security-domain element to validate the role, similar to what can be done in the Webservices or the Remoting subsystem.

Security of JNDI Lookup for initialContext setup

When a JNDI lookup is used to get the initialContext, the security aspect of that operation is managed by the Remoting subsytem.

The Messaging subsystem communicates with your client only after the JNDI lookup is successful.
Refer to the Remoting subsystem section for more information on how to secure it.

Cluster Authentication

If a messaging cluster is used, authentication must be in place to prevent unauthorized nodes joining the cluster pool.
Setup you password in the <hornetq-server> element:

<hornetq-server>
                    ...
                    <cluster-user>myuser</cluster-user>
                    <cluster-password>${jboss.messaging.cluster.password:mypass}</cluster-password>
</hornetq-server>
Properties at startup

It is possible to set up the password and the username as java properties, I.E. jboss.messaging.cluster.password.
This way you may simplify the XML configuration management

Management Interfaces

Native API

Default Native Interface Security

The CLI connects to the Native Management API. For more information on the Native management API refer to https://docs.jboss.org/author/display/AS72/The+native+management+API

HTTP API

Default HTTP Interface Security

The web interface uses the HTTP API to perform tasks. Please refer to the HTTP API for security related operations

JGroups Subsystem

The JGroups subsystem is part of the default JBoss AS 7.2 HA (High Availability) configuration. JGroups is used by other subsystems (I.E. Infinispan) to share data across clusters of Jboss AS.

Unfortunately, the security subsystem is not integrated with the JGroups subsystem. For this reason, you must set up several configuration parameters that may look redundant.

Enable Encryption

The ENCRYPT protocol uses a keystore in order to encrypt the communication layer of all the other protocols below it.

Before configuring the AS, you must create another keystore to use specifically in JGroups.

Unfortunately, JGroups does not support the keystores generated with the standard JDK keytool. You must create your custom keystore with a java file called KeyStoreGenerator which is included in the demo package of JGroups.

java -cp /your/path/to/jboss/modules/system/layers/base/org/jgroups/main/JGroups-version.jar org.jgroups.demos.KeyStoreGenerator --alg AES --size 128 --storeName FILENAME --storePass PASSWORD --alias MyKey

This example will create a keystore named as specified in FILENAME with the password specified in PASSWORD and a key named MyKey, feel free to experiment with different configuration as described in the JavaDOC of this class.

Then include the ENCRYPT protocol in the standalone-full-ha of your JBoss AS instance.

<stack name="udp">
  <transport type="UDP" socket-binding="jgroups-udp" diagnostics-socket-binding="jgroups-diagnostics"/>
    <protocol type="PING"/>
    <protocol type="MERGE2"/>
    <protocol type="FD_SOCK" socket-binding="jgroups-udp-fd"/>
    <protocol type="FD"/>
    <protocol type="VERIFY_SUSPECT"/>
    <protocol type="BARRIER"/>
    <protocol type="pbcast.NAKACK"/>
    <protocol type="UNICAST2"/>
    <protocol type="pbcast.STABLE"/>
    <protocol type="ENCRYPT">
        <property name="encrypt_entire_message">true</property>
        <property name="sym_init">128</property>
        <property name="sym_algorithm">AES/ECB/PKCS5Padding</property>
        <property name="asym_init">512</property>
        <property name="asym_algorithm">RSA</property>
        <property name="keyPassword">YOURKEYSTOREPASSWORD</property>
        <property name="keyStoreName">path/to/keystore</property>
        <property name="alias">MyKey</property>
        <property name="storePassword">YOURTRUSTSTOREPASSWORD</property>
     </protocol>
     <protocol type="pbcast.GMS"/>
     <protocol type="UFC"/>
     <protocol type="MFC"/>
     <protocol type="FRAG2"/>
</stack>
<stack name="tcp">
  <transport type="TCP" socket-binding="jgroups-tcp" diagnostics-socket-binding="jgroups-diagnostics"/>
     <protocol type="MPING" socket-binding="jgroups-mping"/>
     <protocol type="MERGE2"/>
     <protocol type="FD_SOCK" socket-binding="jgroups-tcp-fd"/>
     <protocol type="FD"/>
     <protocol type="VERIFY_SUSPECT"/>
     <protocol type="BARRIER"/>
     <protocol type="pbcast.NAKACK"/>
     <protocol type="UNICAST2"/>
     <protocol type="pbcast.STABLE"/>
     <protocol type="ENCRYPT">
           <property name="encrypt_entire_message">true</property>          <!-- Encrypt the entire message sent -->
           <property name="sym_init">128</property>                         <!-- This is the size of the key in bits -->
           <property name="sym_algorithm">AES/ECB/PKCS5Padding</property>   <!-- Symmetric encryption algorithm used -->
           <property name="asym_init">512</property>                        <!-- This is the size of the asymmetric key in bits -->
           <property name="asym_algorithm">RSA</property>                   <!-- Asymmetric encryption algorithm used -->
           <property name="keyPassword">YOURKEYSTOREPASSWORD</property>     <!-- Keystore password -->
           <property name="keyStoreName">path/to/keystore</property>        <!-- the path to the keystore, you can use jboss expansion i.e. {jboss.mykeystore.path} -->
           <property name="alias">MyKey</property>                          <!-- The name of the key generated -->
           <property name="storePassword">YOURTRUSTSTOREPASSWORD</property> <!-- Trustore password -->
     </protocol>
     <protocol type="pbcast.GMS"/>
     <protocol type="UFC"/>
     <protocol type="MFC"/>
     <protocol type="FRAG2"/>
</stack>

It is preferable in big clusters, to use asymmetric encryption, in that configuration the keystore does not need to be copied across the nodes of the cluster. However, there are several differences and considerations to put in account when choosing symmetric or asymmetric encryption. Read more about it here

 You can move the ENCRYPT element up and down trough the protocols stack, this will configure the subsystem to encrypt only the protocols below the ENCRYPT element.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-13 13:29:38 UTC, last content change 2014-06-02 07:15:31 UTC.