CXF – Adding security by IP whitelisting

Creating a simple Interceptor class to enforce IP address based security for an Apache CXF web-service.

If we know there to be only a small, defined number of remote hosts permitted to use a server-based application, IP whitelisting can be a useful and lightweight way of securing it.

In Whitelisting IP subnets in Java we created a simple, Spring-friendly utility class to check a presented IP address against a whitelist of approved hosts or subnets. Here we’re going to create a simple Apache CXF Interceptor in order to apply it to web service requests.

Creating our Interceptor

Request handling in CXF is highly configurable and customisable through the use of custom Interceptor classes. These can be plugged into many points in the request (and response) handling chain to add functionality where it’s needed.

An interceptor, at its simplest, is a class which implements the org.apache.cxf.interceptor.Interceptor interface:-

public interface Interceptor<T extends Message> {
    void handleMessage(T message) throws Fault;
    void handleFault(T message);
}

A quick look at the API documentation reveals a huge number of pre-defined base classes and working interceptors which readily suit many common requirements or can be used as a base to create new ones.

We’ll create a new NetworkAddressValidatingInterceptor which uses the NetworkAddressValidator we created earlier and applies it to an inbound request:-

public class NetworkAddressValidatingInterceptor 
        extends AbstractSoapInterceptor {

    private NetworkAddressValidator networkAddressValidator;

    public NetworkAddressValidatingInterceptor(
            NetworkAddressValidator networkAddressValidator) {
        super(Phase.RECEIVE);
        this.networkAddressValidator = networkAddressValidator;
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        String remoteAddress =
                ((ServletRequest)message.getContextualProperty(
                "HTTP.REQUEST")).getRemoteAddr();
        if(!networkAddressValidator.isApproved(remoteAddress)) {
            throw new SoapFault("Not permitted",
                    Fault.FAULT_CODE_CLIENT);
        }
    }
}

Here we’re extending the AbstractSoapInterceptor base class which takes care of most of the boilerplate code for us. The only thing we need to do to keep CXF happy is tell the base class which Phase we want to bind our interceptor to. Since we want to check the IP address as early as possible and we don’t need any message processing to have taken place, we’ll bind it to the initial Phase.RECEIVE phase.

A NetworkAddressValidator implementation is supplied as a constructor argument and our handleMessage function simply pulls the remote address from the ServletRequest (which is fine if we’re deploying our service in a web container) and checks that address with the validator. If validation fails we throw a SoapFault with an error message to return to the caller.

Our interceptor class does exactly what every good framework customisation class should – it contains just the additional logic we need to introduce and little else.

Configuring our interceptor

There are a number of ways interceptors can be registered with the CXF framework. Here we’ll use Spring to handle everything for us:-

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xmlns:cxf="http://cxf.apache.org/core"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://cxf.apache.org/core
           http://cxf.apache.org/schemas/core.xsd
           http://cxf.apache.org/jaxws
           http://cxf.apache.org/schemas/jaxws.xsd">

    <bean id="networkAddressValidator"
            class="com.devsumo.cxf.security.BetterNetworkAddressValidator">
        <constructor-arg index="0">
            <array>
                <value>192.168.0.0/24</value>
                <value>127.0.0.1</value>
            </array>
        </constructor-arg>
    </bean>

    <bean id="networkAddressValidatingInterceptor"
            class="com.devsumo.cxf.security.NetworkAddressValidatingInterceptor">
        <constructor-arg index="0" ref="networkAddressValidator"/>
    </bean>

    <cxf:bus>
        <cxf:inInterceptors>
            <ref bean="networkAddressValidatingInterceptor"/>
        </cxf:inInterceptors>
    </cxf:bus>

    <jaxws:endpoint
        id="exchangeRateService"
        implementor="com.devsumo.cxf.exchangeservice.ExchangeRateServiceImpl"
        address="/exchangerateservice"/>

</beans>

Our first two bean definitions create our NetworkAddressValidator implementation with a whitelist configured for our local subnet and the localhost address, and an instance of our new NetworkAddressValidatingInterceptor class which references it.

Then we use the cxf:bus element to add it into our inbound interceptor chain.

Closing the door

Now if we try to access our exchangerateservice web-service from an unapproved IP address we get the door closed firmly in our face:-

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <soap:Fault>
            <faultcode>soap:Client</faultcode>
            <faultstring>Not permitted</faultstring>
        </soap:Fault>
    </soap:Body>
</soap:Envelope>

IP whitelisting isn’t a bullet-proof security measure and if implementing this sort of thing for real we have to keep an eye on the risks of IP spoofing, but it can be a useful tool in our security armoury and one that’s really easy to implement with CXF.

Leave a Reply

Your email address will not be published. Required fields are marked *