2011/07/05
5 Jul, 2011

Securing Web Service Integration

  • Amila Suriarachchi
  • Architect, Member, Management Committee - Data Technologies - WSO2

Introduction

There are different types of users in many corporate software systems. These different uses has different levels of access to the system and hence software systems should control the access according to the user. The access levels of users can be stored as policies in a file and the system should check against these polices when users making requests to different functionalities. This process is called Authorization in computer security. Before authorizing a user request, the software system should validate the user. In other words, it should be able to identify whether a request comes from a real user or an impersonate one. Typically user details are stored in a centralized location. Hence users are asked to provide some secret informations stored in the user store to verify the authenticity of the user. This process is called Authentication.

In any SOA environment many web services are integrated to provide aggregated functionality. These aggregated web services should always authenticate the user, authorize the requests to the back end with the requested user and secure the communication with the back end services. Rest of this article describes how to develop such a secure system using WSO2 Carbon Platform products namely WSO2 ESB, WSO2 Identity Server (IS) and WSO2 Business Rules Server (BRS).

In a previous article titled as Integrate rules with SOA, we saw how to use WSO2 ESB proxy service to integrate two rule services. But this integrated service is not secured. Both proxy service and back end rule services can be access by any user although out side users only need to access the proxy service which provides the integrated functionality.


The Scenario

WS-Security provides standard authentication techniques such as user name token, X509 signature based authentication and kerborse token based authentication. Out of these techniques user name token is the easiest way, hence we are going to use that technique for this article. In order to have the integrated functionality, users should have access to each and every back end services. The access control policies are written as a XCMAL policy. The policy enforcement is done by using the Entilement mediator which calls the Entilement Service runs inside the WSO2 IS. Entilement Service evaluates the request against the XCMAL policy stored in WSO2 IS server. Finally the access to the back end services are limited only to the ESB proxy service by using mutual Authentication. Full source code with the instructions to set up the system can be found from here.

Applies To

WSO2 IS 3.2.0
WSO2 ESB 4.0.0
WSO2 BRS 1.2.0

Contents

Username Token to Authenticate users

Username Token (UT) which is defined under WS-Security specification, is composed of user name and password. When the UT is enabled to a service it expects client to send the UT as a soap header and service can authenticate the user using that information. WSO2 carbon supports UT even with HTTP basic authentication by converting the POX message received to a soap envelop. Security wizard which comes under the service dash board, can be used to to enable the UT for a particular service and the service is authenticated with the carbon user manager.

Client program

In order to invoke a service to which UT is engaged, client program has to send the soap envelope with the UT header. WSO2 carbon platform uses the Apache Rampart in order to invoke a secured service according to the security policy. The security policy file is used to specify the security requirements of the service. Following code is used to enable the security at the client program.

System.setProperty("javax.net.ssl.trustStore", "client/conf/wso2carbon.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");

CustomerOrderServiceStub stub = new CustomerOrderServiceStub("https://localhost:8243/services/CustomerOrderService");

stub._getServiceClient().getOptions().setUserName("amila");

stub._getServiceClient().getOptions().setProperty(RampartMessageData.KEY_RAMPART_POLICY,
         PolicyEngine.getPolicy(new FileInputStream("client/conf/policy.xml")));

stub._getServiceClient().engageModule("rampart");

UT is send over HTTPS transport to secure the message. Therefore users need to specify a trust store which contains the server certificate (public key) with its password. Since this article uses same keystore as in the server by default it contains the server certificate. User name is given as an axis2 client option which is picked by the rampart. The password is picked from a call back handler which is given in the security policy. Following client program can be used to invoke the service with basic authentication.

System.setProperty("javax.net.ssl.trustStore", "client/conf/wso2carbon.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");

CustomerOrderServiceStub stub = new CustomerOrderServiceStub("https://localhost:8243/services/CustomerOrderService");
HttpTransportProperties.Authenticator authenticator = new HttpTransportProperties.Authenticator();
authenticator.setUsername("amila");
authenticator.setPassword("chinthaka");

stub._getServiceClient().getOptions().setProperty(HTTPConstants.AUTHENTICATE, authenticator);
stub._getServiceClient().getOptions().setProperty(Constants.Configuration.ENABLE_REST, Constants.VALUE_TRUE);

First it creates an Authenticator object and set the user name and password to that. Axis2 commons HTTP transport uses this value to create the HTTP headers. Basic authentication based authentication for UT only works with the messages with content type application/xml. Therefore it is required to enable to REST to message.

XCMAL based policy to Authorize

The CustomerOrderService can be accessed by any authenticated user. But the two back end services namely PriceCalculatorService and OrderSelectorService can only be accessed by the users in priceCalculator and orderSelector roles respectively. In other words only users belongs to priceCalculator role is authorized for PriceCalculatorService and only users belongs to orderSelector role is authorized for OrderSelectorService. This authorization can be expressed using a XACML policy as follows.

<Policy xmlns="urn:oasis:names:tc:xacml:2.0:policy:schema:os"
      xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
      PolicyId="customerOrderPolicy"
      RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:deny-overrides">
    <Description> Policy Customer Order</Description>
    <Target/>
    <Rule RuleId="price_calculator_service_rule"
          Effect="Permit">
        <Target>
            <Subjects>
                <Subject>
                    <SubjectMatch
                          MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue
                              DataType="https://www.w3.org/2001/XMLSchema#string">priceCalculator</AttributeValue>
                        <SubjectAttributeDesignator
                              AttributeId="https://wso2.org/claims/role"
                              DataType="https://www.w3.org/2001/XMLSchema#string"/>
                    </SubjectMatch>
                </Subject>
            </Subjects>
            <Resources>
                <Resource>
                    <ResourceMatch
                          MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue
                              DataType="https://www.w3.org/2001/XMLSchema#string">https://localhost:9443/services/PriceCalculatorService</AttributeValue>
                        <ResourceAttributeDesignator
                              AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id"
                              DataType="https://www.w3.org/2001/XMLSchema#string"/>
                    </ResourceMatch>
                </Resource>
            </Resources>
            <Actions>
                <Action>
                    <ActionMatch
                          MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue
                              DataType="https://www.w3.org/2001/XMLSchema#string">invoke</AttributeValue>
                        <ActionAttributeDesignator
                              AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id"
                              DataType="https://www.w3.org/2001/XMLSchema#string"/>
                    </ActionMatch>
                </Action>
            </Actions>
        </Target>
    </Rule>
    <Rule RuleId="order_selector_service_rule"
          Effect="Permit">
        <Target>
            <Subjects>
                <Subject>
                    <SubjectMatch
                          MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue
                              DataType="https://www.w3.org/2001/XMLSchema#string">orderSelector</AttributeValue>
                        <SubjectAttributeDesignator
                              AttributeId="https://wso2.org/claims/role"
                              DataType="https://www.w3.org/2001/XMLSchema#string"/>
                    </SubjectMatch>
                </Subject>
            </Subjects>
            <Resources>
                <Resource>
                    <ResourceMatch
                          MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue
                              DataType="https://www.w3.org/2001/XMLSchema#string">https://localhost:9443/services/OrderSelectorService</AttributeValue>
                        <ResourceAttributeDesignator
                              AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id"
                              DataType="https://www.w3.org/2001/XMLSchema#string"/>
                    </ResourceMatch>
                </Resource>
            </Resources>
            <Actions>
                <Action>
                    <ActionMatch
                          MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                        <AttributeValue
                              DataType="https://www.w3.org/2001/XMLSchema#string">invoke</AttributeValue>
                        <ActionAttributeDesignator
                              AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id"
                              DataType="https://www.w3.org/2001/XMLSchema#string"/>
                    </ActionMatch>
                </Action>
            </Actions>
        </Target>
    </Rule>
</Policy>

As in any other authorization manager XCMAL uses subject, resource and action parameters to specify authorization policies. For this scenario it uses two rules to specify authorization policy. Since this is a web service action is always set to invoke.

This policy can be enforced within a proxy service using entitlement mediator. XACML policy is given in the WSO2 IS and the policy service can be accessed by any other server using Entitlement Service. Therefore location of this service, user name and password should be given in the entitlement mediator as follows.

<entitlementService remoteServiceUrl="https://localhost:9444/services" 
    remoteServiceUserName="admin" remoteServicePassword="admin" callbackClass="org.wso2.sample.CustomerOrderEntitlementCallback"/>

At runtime entitlement mediator sends the subject, resource and action details to the Entitlement service. The callbackClass can be used to give more specific details about these parameters. For this scenario we use the CustomerOrderEntitlementCallback class to send the user name obtain from the UT token, resource id given as a sequence property and set the action as invoke.

public class CustomerOrderEntitlementCallback extends UTEntitlementCallbackHandler {

    public String getUserName(MessageContext messageContext) {
        if (messageContext.getProperty("userName") == null) {
            String userName = super.getUserName(messageContext);
            messageContext.setProperty("userName", userName);
            return userName;
        } else {
            return (String) messageContext.getProperty("userName");
        }
    }

    public String findOperationName(MessageContext messageContext) {
        // we don't worry about the operation so return null
        return null;
    }

    public String findServiceName(MessageContext messageContext) {
        return (String) messageContext.getProperty("resourceID");
    }

    public String findAction(MessageContext messageContext) {
        return "invoke";
    }
}

Apache Rampart store the authenticated user in the message context as a property. However this property is only available in the out sequence path. For other paths it is required to save the user name with synapse context and reuse it.

Protecting Back end Services

Upto now we have secured the proxy service using a UT and the access to back end services through the proxy service. But if some one directly access the back end service there is no protection. These back end services can be secured using Mutual Authentication.

This technique can be used if the back end services are in a different server. Mutual Authentication requires clients to provide their certificate and authenticate with the back end service. For all WSO2 carbon platform products, it is possible to enable mutual authentication by setting the clientAuth parameter in the mgt-transports.xml. Once this parameter is set only a client with valid certificate can be accessed this service. However it is possible to directly access this service within the WSO2 ESB since WSO2 platform uses same keystore file in all products. In a practical situation it is required to import the back end server certificate to WSO2 ESB server key store and WSO2 ESB certificate to back end service keystore.

Conclusion

Security is one of the major concern of any software system. Web service integration is commonly used to form integrated functionality. In such an integrated web service can have a complex authorization logic depending on the services being integrated. Therefore this article describes how to implement such a authorization system using WSO2 ESB as the service integrator and WSO2 IS as the authorization manager. The authorization policy is written as a XACML policy file. Further it shows how users can be authenticated at the integrated service layer and how to secure the back end when running as a different server as well within the same server. It uses the UT in oder to authenticate the user and mutual authentication to secure the back end services.

Author

Amila Suriarachchi, Software Architect, WSO2 Inc.

 

About Author

  • Amila Suriarachchi
  • Architect, Member, Management Committee - Data Technologies
  • WSO2 Inc.