Caucho maker of Resin Server | Application Server (Java EE Certified) and Web Server


 

Resin Documentation

Aug 2012: Resin outscales C-based web server nginx in AutoBench benchmark
Feb 2012: NetCraft survey says Resin experiencing strong growth in last year and used in number of the Million Busiest Sites.
home company blog wiki docs 
app server web server 
health cloud java ee pro 
 Resin Server | Application Server (Java EE Certified) and Web Server
 

resin security


Resin has a comprehensive security framework for application authentication, authorization and transport level SSL based security. Authentication capabilities include built-in support for security data stored in XML files, the database, JAAS, LDAP or properties files, HTTP basic authentication, form based authentication and HTTP password digests. The authorization features include traditional role based security as well as robust conditionals based-on cookies, HTTP headers, locale, IP address and the like. The security framework also supports single sign-on shared across multiple web applications.

Overview

Resin includes a comprehensive security framework for application authentication, authorization and transport level SSL based security. Authentication capabilities include built-in support for security data stored in XML files, the database, JAAS, LDAP or properties files, HTTP basic authentication, form based authentication and password digests. The authorization features include traditional role based security as well as robust conditionals based-on cookies, HTTP headers, locale, IP address and the like. The security framework also supports single sign-on shared across multiple web applications.

This document covers Resin's authentication and authorization capabilities while Resin's SSL support is described in detail here.

The basic structure of the Resin security framework can be demonstrated through a simple example:

WEB-INF/resin-web.xml Basic Security Framework Example
<web-app xmlns="http://caucho.com/ns/resin"
            xmlns:resin="urn:java:com.caucho.resin">
  <!-- The authentication method -->
  <resin:BasicLogin/>

  <!-- Authorization -->
  <resin:Allow url-pattern="/foo/*">
    <resin:IfUserInRole role="user"/>
  </resin:Allow>

  <!-- Authentication provider -->
  <resin:XmlAuthenticator password-digest="none">
    <resin:user name="Harry Potter" password="quidditch" group="user,gryffindor"/>
    <resin:user name="Draco Malfoy" password="pureblood" group="user,slytherin"/>
  </resin:XmlAuthenticator>
  
</web-app>

Most usages of the Resin security framework will follow the general outline of the example above. In the example, the <resin:Allow> tag enables authorization whereas the <resin:IfUserInRole> applies role-based authorization. Users actually enter login/authentication information through HTTP basic authentication in the example above. The authentication information that the user enters is checked against an authentication provider. In the example, the authentication information, including user name, password and groups, are stored in XML (the passwords above are simple text, but they need not and should not be). Note, authentication can sometimes be entirely unnecessary, especially while using conditional authorization rules not specific to a user - such as a condition allowing or denying a set of IP addresses from accessing a URL.

Authenticators

Authentication is the process of verifying that a user is who they say they are. The most common way of verifying the identity of a user is through user name and password. As demonstrated in the example, Resin uses authenticators to verify user credentials. Authenticators look for authentication data matching a login in a back-end resource such as a database or LDAP directory.

The following are the authenticators Resin currently supports:

Authenticators
NAMEDESCRIPTION
<resin:DatabaseAuthenticator> This authenticator works with authentication information stored in a relational database and uses JDBC.
<resin:JaasAuthenticator> This authenticator can be used to plugin-in any Java authentication service (JAAS) module into Resin, including the JAAS modules built into the Sun JDK.
<resin:LdapAuthenticator> This can be used with authentication data stored in LDAP and uses JNDI under the hood.
<resin:PropertiesAuthenticator>This authenticator can use credentials stored in properties files.
<resin:XmlAuthenticator>Uses data stored in XML (either in-line or in a separate file).
AbstractAuthenticator This is an abstract class you can use to create your own Resin custom authenticator.

Each authenticator is described in detail here, including example code that you could use as a starting point for your application. The built-in authenticators should satisfy a large number of common cases, but you can easily create your own custom authenticator when needed.

Resin supports single sign-on in the form of authenticators at the server or virtual host level shared across multiple web applications. This described in detail with authenticators if this is functionality you need for your application.

Securing Resin Administration

The Resin security framework and the authenticators above are intended for application security. However, Resin resources such as /resin-admin as well as the Resin clustered managemnt and deployment administrative features also needs to be secured. Resin internally uses the security framework to secure these resources.

The <resin:AdminAuthenticator> tag is used to secure all Resin resources such as /resin-admin. The admin authenticator is defined only once in the resin.xml file. The authenticator uses the exact same syntax as the XmlAuthenticator.

Resin's top-level <resin:AdminAuthenticator> tag is essentially a static, XML-based authentication context. The authenticator is automatically shared for all hosts and web-apps, so simple sites can even use this authenticator configuration for their site-wide authentication.

Here is a basic example of the Resin admin authenticator:

resin.xml
<resin xmlns="http://caucho.com/ns/resin"
          xmlns:resin="urn:java:com.caucho.resin">
  ...
  <resin:AdminAuthenticator>
    <user name="admin" password="MD5HASH=="/>
    ...
  </resin:AdminAuthenticator>
  ...
</resin>

Securing Passwords with Digests

Passwords in clear-text form are a major security vulnerability. Such passwords can be stolen during transmission or storage and used malicicously (such as in order to gain unauthorized access to back-end resources). The transmission vulnerability is caused by the fact that passwords are sent across the network to the server from the browser in plain text when HTTP basic authentication or form-based authentication is used. This vulnerability can be addressed by either using HTTP digest authentication (covered here) or by using transport layer SSL security (covered here).

You can secure passwords in storage by using the password digest feature built-into Resin authenticators (see the password-digest attribute). You can use the password-digest attribute to specify that the authenticator should use passwords in a secure fashion. When this feature is enabled, the authenticator will store the password in digest instead of clear-text form. When the authenticator receives a clear-text password, it will digest it before comparing it to a stored password for a match.

A digest of a clear-text password is calculated when it is passed through a one-way hashing function that consistently produces another series of characters, digestPassword = digester(username + ":" + realm + ":" + cleartextPassword). The function is "one-way" because the digestPassword cannot be used to practically reverse-engineer the original password.

Resin's authenticators use "MD5-base64" and a realm "resin" to digest passwords by default. MD5 indicates that the MD5 hashing algorithm is used. base64 is an encoding format to apply to the binary result of MD5. You can create an MD5/Base64 digest yourself with a simple PHP script like this:

Calculating a Digest Using PHP
<?php

  $username = "harry";
  $password = "quidditch";
  $realm = "resin";

  echo base64_encode(md5("$username:$realm:$password", true));

?>  

The following are some examples of passwords digested by Resin:

USERNAMEREALMPASSWORDDIGEST
rootresinchangemej/qGVP4C0T7UixSpKJpTdw==
harryresinquidditchuTOZTGaB6pooMDvqvl2Lbg==
hpotterresinquidditchx8i6aM+zOwDqqKPRO/vkxg==
filchresinmrsnorrisKmZIq2RKXAHV4BaoNHfupQ==
pinceresinquietpleaseTxpd1jQc/xwhISIqodEjfw==
snaperesinpotionI7HdZr7CTM6hZLlSd2o+CA==
mcgonagallresinquidditch4slsTREVeTo0sv5hGkZWag==
dmalfoyresinpurebloodyI2uN1l97Rv5E6mdRnDFwQ==
lmalfoyresinmyselfsj/yhtU1h4LZPw7/Uy9IVA==

In the above examples the digest of "harry/quidditch" is different than the digest of "hpotter/quidditch" because even though the password is the same, the username has changed. The Resin digest is calculated with digest(username + ":" + realm + ":" + password), so if the username changes the resulting digest is different.

Calculating a Digest

While using password digests with Resin authenticators, it may occationally be necessary to calculate digests yourself. You can do this in a number of different ways. You could use the PHP script example above. The /resin-admin page includes a form to easily generate the MD5 hash. You can also use the PasswordDigest class to generate the digest programmatically. The following is an example of using this class:

Calculating a Digest - Java example
  import com.caucho.security.PasswordDigest;
  ...
  String username = ...;
  String password = ...;
  String realm = "resin";

  PasswordDigest passwordDigest = PasswordDigest();

  String digest = passwordDigest.getPasswordDigest(username, password, realm);

Unix users can quickly calculate a digest with this script:

echo -n "user:resin:password" | openssl dgst -md5 -binary | uuencode -m -

Disabling the Use of password-digest

Using password digests is so important that all Resin authenticators use it by default. Although it is really not advised, Resin's authenticators can be also be configured to use passwords that are not in digest form. You can do this by specifying password-digest="none" as in the example below:

Disabling the Use of password-digest
<web-app xmlns="http://caucho.com/ns/resin"
            xmlns:resin="urn:java:com.caucho.resin">
  ...	 
  <resin:XmlAuthenticator</type>
    <resin:password-digest>none</resin:password-digest>    
    <resin:user name="harry" password="quidditch" group="user"/>
  </resin:XmlAuthenticator>
  ...
</web-app>

This technique can come in handy for development, testing, etc where password security is not critical.

Setting Password Digest Realm

The realm for Resin authenticators such as the DatabaseAuthenticator and the XmlAuthenticator defaults to "resin". However, if you want, you can explicitly specify the realm to be used for digesting like this:

Specifying a Realm
<web-app xmlns="http://caucho.com/ns/resin"
          xmlns:resin="urn:java:com.caucho.resin">
  ...
  <resin:DatabaseAuthenticator>
    <resin:password-digest-realm>hogwarts</resin:password-digest-realm>
    ...
  </resin:DatabaseAuthenticator>
  ...  
</web-app>  

Single Sign-on

Single sign-on refers to allowing for a single login for more than one context, for example, logging into all web-apps in a server at once without having to re-enter authentication information. Resin allows single sign-on by allowing you to place an authenticator at the host or server/cluster level instead of at the web-app level. The shared authenticator then applies to all the web applications under the host or server. Once you authenticate, the login will last for all the web-apps in that environment/scope.

For example, to configure an XML authenticator for all the web-apps in foo.com, you might configure as follows:

Single Sign-on for foo.com
<resin xmlns="http://caucho.com/ns/resin"
      xmlns:resin="urn:java:com.caucho.resin">

  <cluster id="app-tier>
    <http port="8080"/>

    <host id="foo.com">
      <root-directory>/opt/foo.com</root-directory>

      <!-- Shared across the host -->
      <resin:XmlAuthenticator">
        <!-- password: quidditch -->
        <resin:user name="harry" password="uTOZTGaB6pooMDvqvl2LBu" group="user,gryffindor"/>
	
        <!-- password: pureblood -->
        <resin:user name="dmalfoy" password="yI2uN1l97Rv5E6mdRnDFDB" group="user,slytherin"/>
      </resin:XmlAuthenticator>

      <web-app-deploy path="webapps"
           expand-preserve-fileset="WEB-INF/work/**"/>
    </host>
  </cluster>
</resin>

Any .war in the webapps directory will share the same login across the host. Note, you will still need to implement a login-config for each web-app in addition to login managers/authorization.

The value of reuse-session-id must be true for single sign-on.

Single Sign-on for Virtual Hosts

The basis for establishing client identity is the standard JSESSIONID session cookie. If single sign-on is desired for virtual hosts, Resin must be configured to notify the browser of the proper domain name for the cookie so that the same JSESSIONID cookie is submitted by the browser to each virtual host.

In this case, an authenticator is placed at the cluster level so that it is common to all virtual hosts. The cookie-domain is placed in a web-app-default at the cluster level so that it is applied as the default for all webapps in all virtual hosts.

The following example shows how you can configure single sign-on for two different sub-domains:

Single Sign-on for gryffindor.hogwarts.com and slytherin.hogwarts.com
<resin xmlns="http://caucho.com/ns/resin"
        xmlns:resin="urn:java:com.caucho.resin">
	
  <cluster id="app-tier>
    <http port="8080"/>

    <!-- Shared across all hosts -->
    <resin:XmlAuthenticator">
      <user name="Harry" password="..."/>
    </resin:XmlAuthenticator>

    <web-app-default>
      <session-config>
        <cookie-domain>.hogwarts.com</cookie-domain>
      </session-config>
    </web-app-default>

    <host id="gryffindor.hogwarts.com">
      ...
    </host>

    <host id="slytherin.hogwarts.com">
      ...
    </host>
  </cluster>
</resin>

Because of the way that browsers are restricted by the HTTP specification from submitting cookies to servers, it is not possible to have a single sign-on for virtual hosts that do not share some portion of their domain name. For example, "gryffindor.com" and "slytherin.com" cannot share a common authentication.

Login Managers

Authenticators manage how authentication data is stored, how the user-provided login information is matched to the stored authentication information and how an authenticated principal is constructed. A login manager, on the other hand, controls how the login information is actually collected. HTTP basic authentication is the simplest authentication method (the variety that causes a login/password prompt to appear on the browser when you access a URL). The following are the login managers Resin currently supports:

Login Managers
NAMEDESCRIPTION
<resin:BasicLogin>HTTP basic authentication.
<resin:DigestLogin>HTTP digest authentication.
<resin:FormLogin>Form-based authentication.
AbstractLoginAbstract class for custom login managers.

Each login manager is described in detail here, including example code that you could use as a starting point for your application. The built-in login managers should satisfy a large number of common cases, but you can easily create your own custom login manager when needed.

Authorization

Authorization is the process of verifying that an authenticated user has the appropriate privileges to access a secure resource. The typical authorization process verifies that a user has the right set of permissions to access a URL or is assigned to the correct role/group.

Resin has a very robust set of built-in authorization rules including conditionals, role-based security and combining/chaining rules. Authorization rules are based on a basic URL pattern based top level <resin:Allow>/<resin:Deny> tag set. Any conditionals, if applicable, are nested within these top level tags:

Basic Authorization
NAMEDESCRIPTION
<resin:Allow>Allows access to a URL pattern.
<resin:Deny>Denies access to a URL pattern.

The allow/deny high-level directives can be qualified through a set of conditionals that include the most common case of role-based security (shown in the initial example):

Basic Conditions
NAMEDESCRIPTION
<resin:IfAuthType>Checks for the authentication type, request.getAuthType().
<resin:IfCookie>Checks for the presence of a named HTTP cookie from request.getCookies().
<resin:IfCron>Matches if the current time is in an active range configured by cron-style times.
<resin:IfFileExists>Matches if the URL corresponds to an actual file.
<resin:IfHeader>Tests for a HTTP header and value match.
<resin:IfLocale>Tests for a Locale match from the HTTP request.
<resin:IfLocalPort>Compares the local port of the request, request.getLocalPort().
<resin:IfMethod>Compares the HTTP method, request.getMethod().
<resin:IfNetwork>Compares the remote IP address to a network pattern like 192.168/16.
<resin:IfQueryParam>Tests for a HTTP query parameger, request.getParameter().
<resin:IfRemoteAddr>Tests against the remote IP address, request.getRemoteAddr().
<resin:IfRemoteUser>Tests against the remote user, request.getRemoteUser().
<resin:IfSecure>True for SSL requests, i.e. if request.isSecure() is true.
<resin:IfUserInRole>Tests is the user is in the role.
RequestPredicateInterface for custom request predicates.

These conditionals can also be combined/chained as needed using the following tags:

Combining Conditions
NAMEDESCRIPTION
<resin:And>Matches if all children match.
<resin:Or>Matches if any children match.
<resin:Not>Matches if the child does not match.
<resin:NotAnd>Matches if any child does not match.
<resin:NotOr>Matches if all the children do not match.

Each authorization tag is described in detail here, including example code that you could use as a starting point for your application as well as common usage patterns. The built-in rules should satisfy a large number of common cases, but you can easily create your own custom predicate when needed.


Copyright © 1998-2012 Caucho Technology, Inc. All rights reserved. Resin ® is a registered trademark. Quercustm, and Hessiantm are trademarks of Caucho Technology.