Friday, February 7, 2014

Securing your REST API



With developers exposing application functionality as REST services, willy nilly all over the enterprise, the onus of securing these REST services is on them as well. Here I will discuss the options for means of authenticating your REST services. For authenticating REST services you have the following broad options:


  1. Use HTTP Basic Auth, Digest, Form based or Client Certificates
  2. Use custom token based authentication mechanisms
  3. Use OAuth 2.0 - Two legged or Three Legged Auth


Using HTTP Basic Auth, Digest, Form based or Client Certificates
Since HTTP is the most popular implementation of REST, Java EE based authentication mechanisms hold true for REST service calls as well. Here we have 3 options, as mentioned well in Java EE Security documentation.

To secure REST API exposed at http://<hostname>:<portnumber>/web-app-context/webapi/rest-resource we will use the following configuration in web application's web.xml




Above, configuration uses the "Basic Auth" scheme, similarly REST based (logical) resources, can be secured using DIGEST, Form Based and client-certificates schemes as well. Please refer Java EE security documentation for further details.

Also, suitable realm can be used to refer to credential stores. If you want to make the actual authentication to be portable across application servers you can use JAAS and write-up your own custom LoginModule and CallbackHandler.

So far so good, securing REST services is like any conventional Java EE web application. But....

Many times, the REST services are consumed by not conventional browser based UIs, but rather by mobile applications based on say android or iOS. In such cases, having a mobile app which needs a re-login after  HttpSession timeout of say 30 minutes(for tomcat), can be very tedious and undesirable. Instead most mobile apps allow user to enter the username and password, once and then store it on the mobile device as an app preference.

In order to provide a feature such as above, with mobile clients in mind, we can use "custom tokens with expirations". The idea itself is quite simple.

  1. The first time a mobile user uses the app, he is asked for a username and password for authentication. 
  2. This server backend provides a REST API such as /login which takes a Http POST and username and password as part of Http request body. 
  3. The password itself can be sent across using encoding such as base-64 (over SSL obviously!) 
  4. The server validates the username and password going against a credential store such as LDAP based or ActiveDirectory based. Once the user is authenticated, a unique token is generated by server, the server saves this token against the username, along with say a timestamp and expiry date time and also the token is sent back to the client.
  5. The client stores this token say in HTML5 localstorage and uses it in subsequent REST requests, in a custom http header
  6. For subsequent requests, the server looks at the custom header retrieves the token, validates that it is valid by checking against the username and also ascertaining that it has not expired as per expiry policy.
  7. If token is valid, the request goes through, if token is invalid or expired, the server throws back a 401 error, which makes the client go back to login page for re-login
The above mechanism is depicted in representative code below, to clarify the explanation.

The server side authentication and token check can be done using, a servlet filter interceptor as depicted below:

package com.mastek.opensource.security.filters;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SecurityTokenFilter implements Filter {
 
 @Override
 public void doFilter(ServletRequest req, ServletResponse resp, 
   FilterChain chain) throws IOException, ServletException {
  
  HttpServletRequest request = (HttpServletRequest)req;
  String token = request.getHeader("MyTokenField");
  
  //additionally check if the token is valid
  //valid can also check if token is expired as per policy
  if(token == null || token.length() == 0)  
     //send HTTP resp code 401: Unauthorized
     ((HttpServletResponse)resp).setStatus(HttpServletResponse.SC_UNAUTHORIZED); 
  else
     chain.doFilter(req, resp);
  
 }


 @Override
 public void destroy() {

 }
 
 @Override
 public void init(FilterConfig arg0) throws ServletException {

 }
 
}


The servlet filter entry in web.xml is shown below:

The ajax request which sends out the token in custom Http header and redirects to login page if server throws a 401 is depicted below:


$(document).ready(function() {
$.ajax({
    type: 'GET',
    url: "my-url",
    dataType: 'json',
    data: "{ 'name': 'somedata'}",
    beforeSend: function (request){
        request.setRequestHeader("MyTokenField", localStorage.userToken);
    },
    success: function(mydata) {
        console.log('data ret='+JSON.stringify(mydata)) 
    },
    error: function(mydata){
        if(mydata.status === 401){
            alert('Redirecting you back to login page....');
            window.location = "login.html";
        }
    }
});
 
});

I have'nt put up the /login API, but its easy to imagine that, as a service which takes in username and password, and returns back a token.

The above mechanism is still suspectible to man-in-the-middle attacks, for which the transport needs to be secured with SSL, any which ways. Libraries for generating hash as the unique token are available in javascript as well as java.

The flexible thing about custom tokens, is that you can put in code on your server side to implement any particular policy for the token expiration. Example tokens can be to expire in a day or in a week. Also, keeping track of active users in your system is easier.

I will leave the OAuth 2.0 two-legged mechanism for a subsequent article, since this one is getting too lengthy as it is :-)

Hope you enjoyed the discussion above, related to basics of securing REST services.

Cheers!

No comments: