Propagating web application credentials through JAX-WS and Spring Security
I am a big fan of EJB3, but I think certain integration realities in an enterprise environment can limit its relevance. While recently working with EJB3 stateless session beans exposed as JAX-WS web services, I ran into some issues propagating security credentials. I know that this is a fairly seamless process in a traditional Java -> EJB invocation. I had hoped that there was some magic that would provide the analagous solution of security credential propagation from a Java web application to a JAX-WS web service secured with HTTP basic auth (and SSL for the transport mechanism).
I could not dig up any details on this topic, so I came up with my own solution by assembling standard and quasi-standard components.
The end result looked like this:
1. The web application, which used form security, was adapted to use the Spring Security form-based authentication. Although this was a somewhat painful process in the Acegi Security days of configuration hell yore, the Spring Security domain-specific namespace has made it a quite simpler activity. You need a couple of filters and a very simple Spring XML file. Reference docs are here. Anyway, I set up the authentication in the web app to authenticate against the same JAAS realm I had previously used with straight container-managed security.
2. Next, I used the Spring JaxWsPortProxyFactoryBean to wire up my remote web service to appear as a simple business service interface to the web application code. This is pretty cool, because it lets you code your view and controller code completely ignorant of the fact that the model lives somewhere in the SOAP world. I leveraged the new basic authentication functionality of Spring 2.5.5’s JaxWsPortProxyFactory bean and created session-scoped instances. So, I could have an instance of the client bean for each user of the web application, and that client would have its username and password properties initialized from the Spring Security SecurityContext object in scope.
3. Next, I set up my web service (JAX-WS servlet endpoint with injected EJB resource) with Spring Security settings of its own, to include basic authentication against the same realm as the web application (if on different containers, would be different realms but the same underlying authentication store) and HTTPS transport. Then, voila, I had credentials that were equally valid in the web application and the web services.
To be fair, this was a lot of work, but code samples aren’t really necessary as most of the work was in figuring out a valid approach
However, I believe the benefits will be immense in the long run.
I can always add rules later to enable anonymous callers of certain web service operations, but more often than not, I want to know who the caller is and have their experience (returned data) take that into account. That way I can take advantage of all the neat things that Spring Security offers to create fine-grained security in my services. I can do method-level security, collection-filtering security, and audit logging with ease! At the most primitive level of functionality, I can do a thumbs-up, thumbs-down decision on whether a user is even authenticated and allowed to use a web service.
I have seen some pretty primitive attitudes toward web service security–things like non-SSL transports combined with a simple authentication token that never changes. This is exactly the kind of recklessness I wanted to avoid in my services. SOA is a powerful mechanism when employed properly. One of the premises of SOA is that systems can be easily built by aggregating services. However, that is no reason for the web services to be a free-for-all for anyone who can find a “Hello, this is Axis” servlet and associated WSDL.
Stand with me and build a better web service!
Tags: EJB, JAX-WS, SOAP, Spring Security (formerly Acegi Security)
February 7th, 2010 at 4:27 am
You said:
What exactly do you mean by the bit about “simple authentication token”?
Do you mean like a mutually defined token, used on the web service, and the client?
Or do you mean a token generated by an authentication of principal/credential passing on the first request?
Thanks.
February 7th, 2010 at 11:12 am
Trent, I have seen this done about every way you can do it, most of which made me cringe when I studied the implementation and gained an understanding of its ramifications.
I have seen cases where the username was the only thing that meant anything, and the token was essentially a pre-shared value known to the client and server and written into Java code (identical for all users!). That type of implementation would only even approach feasibility if the users were never logging in themselves, but some sort of automated system was using the web service on their behalf.
Clearly, a token generated by an authentication of principal/credential on the first request is a much better system. I would still caution against using that approach without an SSL transport unless you have expiration rules built into that token (such as a nonce).
Although there are definitely holes in some of the approaches I have seen, most of them could be rationalized if they employed some sort of securing of the message, be it an SSL transport, message encryption, or both. I know in many cases, the message itself might not be secure due to the nature of the business–something like a pay-per-result public web service with fairly mundane inputs and outputs. In this case, the truly secure data is the username and authentication information as those are tied to billable events. These are the cases where expiring tokens and other aggressive strategies should definitely be employed.
As with all things security, I would advise looking at your implementation and trying to think of the worst things that could go wrong. If you are working for a high-profile organization or project, it is just a matter of time until someone exploits it. I have even seen security holes intentionally exploited by integration partners because it made the implementation easier.
Best of luck, and feel free to comment again. I hope this was helpful.
February 7th, 2010 at 8:37 pm
I completely agree with you. Without SSL, token passing in any way is not advised. And creating a pre-shared token is virtually useless if someone hacks the front end web server. But, so is SSL client certificates, assuming the system is “rooted”.
I would say, that one of the best methods, is a combination of SSL, and a token that is generated on the farthest back SOA server from the DMZ, after the user has been authenticated with username/password, or some other method. That way, the “authenticated” state is established by a more secure server, and the username and password aren’t continually being sent back and forth, which is ideal.
Any thoughts?
February 8th, 2010 at 8:10 pm
Yes, that certainly sounds prudent. I am currently looking at preparing a system in development for production readiness with additonal security and optimizing authentication to be less “chatty.” The system has hundreds of web service calls in short intervals and it is currently providing the username and password to the web service with each request. Those are then validated against a separate LDAP backend using a bind operation. In other words, not terribly secure and it’s doing too much work also.
One thing I am considering is generating an encryption key at start up of an authentication server known only to it. When an authentication operation completes, the result would be a ticket. The ticket would basically be an encrypted value representing the username and additional information to facilitate expiration and uniqueness over time (any token that doesn’t change is inherently insecure). When provided with that information, the authentication service would do a lookup by the decrypted username to retrieve cached principal information. The principal cache would either be refreshed at a time interval or updated through event propagation.
Ideally, I would like to find an open source ticketing server that plays well with all the various technologies in the system (Mule, Spring, JAX-WS, CXF, etc.). I feel like I understand the difficulties of security conceptually, but I always try to leverage experts when possible. Just keeping up with what encryption algorithms to employ is a moving target with all the continual advances in attacks.