Simple Security for HTTP Based RESTful Services (Part 2)

In part one of this series, we examined one potential solution that turned out to be not so good. In this post lets try to find a solution using the same design, but a better implementation.

To recap, we decided to use the simplest solution which is to add a security hash to each service call. The first approach we tried was adding the security hash to the XML payload of each service call. The conclusion was that approach was not very elegant, and actually quite ugly.

Let us now discuss a more elegant solution. Keeping the same parameters in place, namely, we want to pass the hash along with each service request, where can we place it where it will not cause the implementation to be ugly? Why don’t we try by add the Hash as a field in the Http header?

The request would look like:

PUT http://blah.com/services/someEntity/226 HTTP/1.1
Accept: text/xml
Content-Type: text/xml
User-Agent: Jakarta Commons-HttpClient/3.1
Host: somehost:port
Hash: 53b383c67a03d23a38b6f52f4a732553
Content-Length: 380
<someEntity>
    <fieldOne> fieldOne </fieldOne>
    <fieldTwo> fieldTwo </fieldTwo>
</someEntity>

As you can see, the hash has been added to the Http header (line 6). To verify the hash, we need to do create a Servlet Filter that verifies the Hash.

A very simple filter would look like:

public class VerifyHashFilter implements Filter {
   public void doFilter(ServletRequest request,
            ServletResponse response,FilterChain chain)
            throws IOException, ServletException {
     if(((HttpServletRequest)request).getHeader("HASH").equals("12345"))
        chain.doFilter(request,response)
     else
        ((HttpServletResponse)response).setStatus(401);
   }
}

Then our service code implementation would change to:

@Path("/services")
public class SomeEntityServiceImpl {

    @PUT
    @Path("/someEntity/{id}")
    @Consumes("text/xml")
    public void updateSomeEntity(SomeEntity entity,
                @PathParam("id") int id) {
    dao.saveOrupdate(entity)
    }
}

To test our service, we can use curl:

$ curl -i  -H 'HASH:12345' http://localhost:9095/rest-test
HTTP/1.1 200 OK
Content-Type: application/xml
Content-Length: 71
Server: Jetty(6.1.14)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<someEntity>
    <fieldOne>fieldOne</fieldOne>
    <fieldTwo>fieldTwo</fieldTwo>
</someEntity>

And the negative test:

$ curl -i  -H 'HASH:54321' http://localhost:9095/rest-test
HTTP/1.1 401 Unauthorized
Content-Length: 0
Server: Jetty(6.1.14)

As you can see, the implementation code is much cleaner and reusable now. The service code can be called from anywhere within the application without having to worry about a security hash. It is safe to assume that if the code makes it as far as the service, it passed through the security filter thus making the request authorized. Having that assumption as part of the design allows you to write much more generic and reusable code.

Please keep in mind this concept is nothing new, as this solution is an over simplification of how the Spring Security module works. The value in this solution is it doesn’t have dependencies on any external frameworks. When designing this solution, that was one of the determining factors.

Next time you need to implement a quick authorization mechanism for your REST service, make sure you keep the Http header along with a Servlet filter in mind.

comments powered by Disqus