Copyright © 2001-2007 Nicholas Quaine.
Home   Bases   Côté-Serveur   Côté-Client   Démos   FAQ   Ressources
Home
Bienvenue
A propos de ce site
A propos des auteurs
Nous contacter
Plan du site
Sites Partenaires
XMethods
Top XML SOAP Zone

Article
Sécurité avec Apache SOAP

Cette page est disponible uniquement en anglais.

In this article we will explore the options that you have when you want to add security to your web services. With a reasonable amount of work, we will transform a simple service into an acceptably secure one. And with security, we will even go further and think about real business.

User Authentication

This is the most basic, and simple form of 'security'. Most web sites that ask for a username and login to customize your welcome page rely on this feature. In fact, it is not secure at all, but in most cases, it will do the job.

It is possible to send username and password information in the HTTP header of a SOAP request. To do this, the Apache-SOAP API allows setting a SOAPHTTPConnection object with username and password. These values are included in the HTTP header in the form of a simple string "Authorization: Basic username:password" that is Base64 encoded. This gives the following HTTP header for an 'authenticated request'.


  POST /apache-soap/servlet/rpcrouter HTTP/1.0
  Host: 127.0.0.1
  Content-Type: text/xml; charset=utf-8
  Content-Length: 538
  SOAPAction: ""
  Authorization: Basic c2Jlbm9pc3Q6c3R1cGlkcHdkMQ==

And that's all there is to it. So, why is it so unsafe? Well, anybody can use the TcpTunnelGUI tool that is provided with the Apache SOAP toolkit to view HTTP headers. And it takes just a few lines of code to decode a Base64 encoded string, as we will see now in the Server changes.

Server Changes

To make your Apache-SOAP server read this username and password information, you will have to use a custom provider instead of the standard provider that does the job for Java classes. If you look into the Apache SOAP toolkit classes, there is a class, org.apache.soap.providers.RPCJavaProvider. This is the class that Apache uses as a provider whenever you deploy a java class as a service. The toolkit also includes a class named org.apache.soap.providers.TemplateProvider that you can use as a base to write your own provider. The complete code for our provider is available in Listing 1. Any provider in Apache SOAP must implement the Provider interface, which defines two methods, locate and invoke. To do our authentication checks, we will modify the locate method of our provider, and extract the Authorization header. The code required to decode username and passwords is very simple, as shown below.


  HttpServletRequest rq = (HttpServletRequest)
        reqContext.getProperty(Constants.BAG_HTTPSERVLETREQUEST);
  String auth = rq.getHeader("Authorization");
  auth = auth.substring(auth.indexOf(" "));
  // Decoding is as simple as this...
  String decoded = new String(Base64.decode(auth));
  // decoded now contains username:password in plain text.
  int i = decoded.indexOf(":");
  // so we take the username from it ( everything until the ':' )
  String username = decoded.substring(0,i);
  // and the password
  String pwd = decoded.substring(i+1,decoded.length());

Now that we have written the service provider, we just need to deploy the service and specify our custom provider. The figure below shows you how to do that with the Web Deployer of Apache SOAP. Look at the 'User-Defined' value for provider type and the classname of our provider just below. Of course you need to make the class available in Tomcat's CLASSPATH by dropping the jar in the lib/ directory.

Client Changes

The code changes to add basic authentication on the client side are simple:


  Call c = new Call();
  SOAPHTTPConnection hc = new SOAPHTTPConnection();
  hc.setUserName("username");
  hc.setPassword("pwd");
  call.setSOAPTransport(hc);

And now you're done. Of course, the implementation of our custom provider is dumb and will not check anything for you. You need to modify the code to add your own authentication verification. It can be implemented using a database application containing user profiles and passwords, or any other system that you may need. It is up to you. Now let's move on to serious things.

HTTP over SSL and Digital Certificates

The first thing to do is to setup the Web Server/Servlet Container to use SSL. In our case, the documentation that explains how to do that for Tomcat is fine, with the exception of the clientAuth parameter on Tomcat's server file that we set to false instead of true.

The most painful part of this exercise is really the certificates part. Certificates are required for both the client and the server and should be verified and trusted to get things to work.

The prerequisites are to get JSSE (Java Secure Socket Extension), for the server and the client.

Let's go, step by step.

  1. Modify TOMCAT to use SSL. In our case, we have set up Tomcat to start the SSL handler on port 8443.
  2. Get Digital Certificates for the client and the server.
  3. Modify the Client code to use https and the certificates:

    
      // Specify the location of where to find key material for the default TrustManager.
      // This overrides jssecacerts and cacerts in the JDK lib directory.
      System.setProperty("javax.net.ssl.trustStore","C:\\YourDirectory\\client.keystore");
    	
      // use Sun's reference implementation of a URL handler for the "https" URL protocol type.
      System.setProperty("java.protocol.handler.pkgs","com.sun.net.ssl.internal.www.protocol");
    
      // dynamically register sun's ssl provider.
      Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
    
      // note that the url is using https protocol and not http.
      URL url = new URL("https://myaddress.com:8443/apache-soap/servlet/rpcrouter");
      Call call = new Call();
      (...)
    

Your data is now protected. At this point,if you try to use the TcpTunnelGUI tool to see how the HTTP request looks like, you will not be able to get anything from it. So that's it, you're done!

Of course, security has a cost... performance. We have found that using SSL was increasing response times by 50%. If you have a profiler tool like JProbe (www.sitraka.com) or OptimizeIt (www.vmgear.com), you can profile your client application and look at the difference, just for curiosity. Establishing SSL connections takes time because of the handshaking protocol, plus random number generation and so on. Anyway, if you need security, you don't really have a choice, do you ?

With such security in place, you can now move on to real serious things, like providing services with real added value, to a very restricted set of people. And if you want to do business, you can probably start billing for access to your service. This is the free gift of the day... There are two options in this case:

  1. You can either change the custom provider that we have used to add billing functions to charge on a per service request. Be careful though, you will need to handle failure cases to avoid billing your customers on failed requests. To do this, you need to modify the invoke method of the provider, and do the billing after the invocation of the service.
  2. If you have complex business rules, you may want to leave your provider generic and delegate the billing work to the service itself. We will once again modify the providers invoke method but in a more powerful way... The targetObject attribute in the method is the actual service itself, so you can for example define an interface "BillingService" and have all your billing services implement it.The rest is straightforward. See Listing 2.

Conclusion

So, you see, that was not so difficult. Even if the SOAP specification does not say anything about security (this may change though), adding security to your services is easy. There are technologies that would cause you much more of a headache before achieving the same result. Have fun with SOAP.

[ Sylvain Benoist ]

Copyright © 2001-2007 Nicholas Quaine. Tout droit reservé.