XWSS

XML Web Services Security Forum - Est. 2002

Securing Web Services: Lessons from the J2EE Platform

Thread started by c_brennan on 2003-07-22. Viewed 5,137 times.

c_brennan - Member since 2002-05-10 - Posts: 274
Posted: 2003-07-22 11:04 UTC

I wanted to start a discussion about securing web services within the J2EE platform, since the intersection of J2EE container security and WS-Security is a topic that does not get enough attention in the specifications or in vendor documentation.

With the release of JAX-RPC 1.1 as part of J2EE 1.4, we now have a standardized API for building SOAP-based web services in Java. However, the security story remains fragmented. The J2EE specification provides container-managed authentication and role-based authorization through JAAS, but these mechanisms were designed for servlet and EJB clients, not for SOAP message processing. The result is that each application server vendor has taken a different approach to bridging the gap between WS-Security tokens in SOAP headers and the J2EE security context.

JAX-RPC Handler Chains for Security Processing

The JAX-RPC specification defines a handler framework that allows request and response messages to be intercepted and processed before reaching the endpoint implementation. This is the natural place to implement WS-Security processing in a J2EE environment. A typical security handler chain would include:

<handler-chains>
  <handler-chain>
    <handler>
      <handler-name>WSSTimestampHandler</handler-name>
      <handler-class>com.xwss.handlers.TimestampValidationHandler</handler-class>
    </handler>
    <handler>
      <handler-name>WSSSignatureHandler</handler-name>
      <handler-class>com.xwss.handlers.SignatureVerificationHandler</handler-class>
    </handler>
    <handler>
      <handler-name>WSSAuthHandler</handler-name>
      <handler-class>com.xwss.handlers.UsernameTokenHandler</handler-class>
    </handler>
  </handler-chain>
</handler-chains>

The challenge is that JAX-RPC handlers operate on the SOAP message (as a SOAPMessageContext), but they have no direct access to the J2EE container's security infrastructure. After validating a UsernameToken or X.509 certificate in the handler, you need a way to establish a JAAS Subject and propagate it into the container's security context so that EJB method-level permissions and isCallerInRole() checks work correctly in the service implementation.

I would like to hear how others are handling this integration, particularly on WebLogic and WebSphere where the proprietary security SPIs differ significantly.

- Craig Brennan

a_petrov - Member since 2002-11-14 - Posts: 189
Posted: 2003-07-23 08:31 UTC
c_brennan wrote:
After validating a UsernameToken or X.509 certificate in the handler, you need a way to establish a JAAS Subject and propagate it into the container's security context.

Craig, this is exactly the problem we ran into on our project last quarter. We are running WebLogic 8.1 SP2 and had to use the WebLogic Security Service Provider Interface (SSPI) to bridge WS-Security tokens to the container security context. The approach is as follows:

1. The JAX-RPC handler extracts the UsernameToken from the wsse:Security header and validates the password digest against our LDAP directory.

2. After successful validation, the handler calls the WebLogic IdentityAsserter SPI to create an authenticated weblogic.security.acl.internal.AuthenticatedSubject for the user principal.

3. The handler then uses weblogic.security.Security.runAs() to associate the subject with the current thread, so that downstream EJB calls inherit the correct security context.

// In the JAX-RPC handler's handleRequest():
Subject subject = authenticate(usernameToken);
weblogic.security.Security.runAs(subject, new PrivilegedAction() {
    public Object run() {
        // forward to next handler or endpoint
        return null;
    }
});

The main drawback is that this ties you completely to WebLogic's proprietary security APIs. There is no portable way to do this across J2EE application servers, which is a significant gap in the platform. On WebSphere 5.0, the equivalent mechanism uses the Trust Association Interceptor (TAI) framework, which has a completely different API and lifecycle model.

For role-based authorization, we define security constraints in weblogic-webservices.xml that map LDAP groups to J2EE roles. Once the JAAS Subject is established through the SSPI, the container's standard @RolesAllowed annotations (or the equivalent deployment descriptor entries) work as expected on the EJB methods backing the web service.

- Alexei Petrov

l_morrison - Member since 2003-01-20 - Posts: 97
Posted: 2003-07-24 15:47 UTC
a_petrov wrote:
On WebSphere 5.0, the equivalent mechanism uses the Trust Association Interceptor (TAI) framework, which has a completely different API and lifecycle model.

I can provide some detail on the WebSphere side. We have been running a SOAP-based order management system on WebSphere 5.0.2 since March 2003, and integrating WS-Security with the WebSphere security domain was one of the more difficult parts of the project.

WebSphere's approach is to use a custom Trust Association Interceptor that intercepts the HTTP request before it reaches the JAX-RPC runtime. The TAI examines the incoming SOAP message (which requires pre-parsing the XML in the interceptor, unfortunately), extracts the WS-Security token, validates it, and then returns a principal name that WebSphere maps to its internal credential representation. The key interface method is:

public TAIResult negotiateValidateandEstablishTrust(
    HttpServletRequest req,
    HttpServletResponse resp) throws WebTrustAssociationFailedException {

    // Parse SOAP envelope from request input stream
    // Extract wsse:Security header
    // Validate UsernameToken or X.509 cert
    String principal = validateWSSecurityToken(req);

    return TAIResult.create(
        HttpServletResponse.SC_OK, principal);
}

The advantage of the TAI approach is that once the principal is established, the entire WebSphere security infrastructure works transparently. EJB method permissions, getUserPrincipal(), isUserInRole(), and even the WebSphere security audit trail all see the correct identity. The disadvantage is that you are parsing the SOAP message twice: once in the TAI for security processing and again in the JAX-RPC runtime for business processing.

We also discovered that the TAI runs before the JAX-RPC handler chain, which means you cannot combine TAI-based authentication with handler-based WS-Security processing (for example, decryption in a handler after authentication in the TAI). For our use case this was acceptable because we terminate SSL at the WebSphere HTTP server and only need message-level authentication, not encryption.

IBM has indicated that WebSphere 6.0 will include native WS-Security support that integrates directly with the security domain, which should make custom TAI implementations unnecessary. Until then, the TAI route is the supported approach.

- Laura Morrison

c_brennan - Member since 2002-05-10 - Posts: 274
Posted: 2003-07-28 09:55 UTC

Thank you both for the detailed responses. The contrast between the WebLogic SSPI approach and the WebSphere TAI approach illustrates the fundamental problem well: each vendor has created a proprietary bridge between WS-Security and the J2EE security context, and there is no portable abstraction.

JAAS LoginModules as a Portable Foundation

One approach we have been exploring is to use a custom JAAS LoginModule as the validation layer, since JAAS is the one security API that is consistent across J2EE application servers. The LoginModule accepts a NameCallback and PasswordCallback populated from the WS-Security UsernameToken, authenticates against the backend directory, and populates the Subject with the appropriate principals and credentials:

public class WSSecurityLoginModule implements LoginModule {
    public boolean login() throws LoginException {
        NameCallback nameCallback = new NameCallback("username");
        PasswordCallback pwdCallback = new PasswordCallback("password", false);
        callbackHandler.handle(new Callback[]{nameCallback, pwdCallback});

        String username = nameCallback.getName();
        char[] password = pwdCallback.getPassword();

        // Validate against LDAP or database
        if (!ldapAuth.authenticate(username, new String(password))) {
            throw new FailedLoginException("Invalid credentials");
        }

        // Query roles from directory
        Set roles = ldapAuth.getRoles(username);
        subject.getPrincipals().add(new UserPrincipal(username));
        for (Iterator it = roles.iterator(); it.hasNext();) {
            subject.getPrincipals().add(
                new RolePrincipal((String)it.next()));
        }
        return true;
    }
}

The LoginModule itself is portable. The non-portable part is configuring the application server to recognize the resulting JAAS Subject as its internal security context. On WebLogic, you still need to register the LoginModule through the SSPI Authentication Provider framework. On WebSphere, you configure it as a custom JAAS login configuration in the admin console. But at least the core authentication and role-mapping logic is written once.

For those interested in where the standards are heading: JSR 921 (the J2EE 1.4 specification) does acknowledge the need for web services security integration, but it defers the details to the individual component specifications (JAX-RPC, EJB, Servlet). The real fix will likely come with JSR 109 (Web Services for J2EE) version 1.2 or later, which is expected to define a standard security binding between WS-Security tokens and the J2EE security context. Until then, we are all working around the gap with vendor-specific solutions.

I have written up a more complete version of this discussion with working deployment descriptors for WebLogic 8.1, WebSphere 5.0, and JBoss 3.2. I will post it as an article in the Articles section once I have verified the JBoss examples on the 3.2.3 release.

- Craig Brennan