How to secure the Rest APIs

sophea Mak
Javarevisited
Published in
6 min readJan 11, 2020

--

Today let’s talk about the security to protect your APIs. In this article, you will learn and understand how to build and secure APIs with various security patterns.

We will discuss the best practices of how to implement security in the REST APIs.

REST-APIs

REST (Representational State Transfer) is truly a “web services” API. REST APIs are based on URIs and the HTTP protocol, and use JSON for a data format, which is super browser-compatible. REST APIs can be simple to build and scale. Rest-APIs is a modern architectural model that is used in web services. It is unlike the previous protocol HTTP and SOAP XML.

Security

Security is a major concern to build RESTful web services.

There are lots of ways to implement security with user authentication and authorization in the RESTful web services.

The main types of security we are going to talk about are the following:

  • API key
  • Basic authentication
  • Oauth2
  • Oauth2 + JWT

In order to make our discussion more specific, let’s assume we build a simple microservice backend application and the client users make requests to our backend services throw the REST-APIs. We will explain the use cases on how to secure the APIs with the context above.

API Key

It is a very easiest and simplest way to apply security and protect the API.

When to use: It fits designing APIs for 3rd parties services integration and limited access, not public access purpose. For instance, your company provides SMS gateway as web services and other companies would like to use your services.

How to use: validate the api-key either request param or Request Header.

How it works: Create Servlet Filter Security and validation either looking at the request param api_key and X-API-Key as HEADER and whitelist IPs address (optional). So every user makes a request, the Filter Security will check and validate first before it continues into the API controller part.

ex : curl 'http://localhost:8080/api/test?api_key=xxxxxx'  -H 'Connection: keep-alive' --compressed/**SecurityFilter.java*/package com.sma.spring.security;import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Sophea <a href='sopheamak@gmail.com'> sophea </a>
*/
public class SecurityFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(SecurityFilter.class);
/**api_key by request parameter*/
private static final String API_KEY_PARAM = "api_key";
/**X-API-Key as request Header*/
private static final String HEADER_NAME_API_KEY = "X-API-Key";

/**whilelistIps*/
private static final List<String> WHITELISTED_IPS = new ArrayList<>();

/**value - you can define either in config or read from db*/
private static final String API_KEY_VALUE = "sd3209Sdkl2DF3dfzsDGEsZ8476";
@Override
public void init(FilterConfig arg0) throws ServletException {
LOG.info("init Security filter");

/**initialize ip address either or you get fromconfig*/
//WHITELISTED_IPS.add("127.0.0.1");
WHITELISTED_IPS.add("192.168.0.1");
WHITELISTED_IPS.add("10.1.2.29");
}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
final String requestUri = request.getRequestURI();

LOG.debug(">> Request method {} - URI : {}",request.getMethod(), requestUri);

LOG.debug(String.format(">> Client's IP address: %s, api_key: %s, X-API-Key: %s", request.getRemoteAddr(), request.getParameter(API_KEY_PARAM),
request.getHeader(HEADER_NAME_API_KEY)));
//check request api_key present ?
if (! (verifyApiKey(request) || verifyIpAddress(request.getRemoteAddr()))) {
LOG.error("Either the client's IP address is not allowed, API key is invalid");
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Either the client's IP address is not allowed, API key is invalid");
return;
}

chain.doFilter(req, resp);
}
/**
* verify api key either request param or request Header
* @param request
* @return
*/
private boolean verifyApiKey(HttpServletRequest request) {
return API_KEY_VALUE.equals(request.getHeader(HEADER_NAME_API_KEY))
|| API_KEY_VALUE.equals(request.getParameter(API_KEY_PARAM));
}


/**
* verify api key either request param or request Header
* @param request
* @return
*/
private boolean verifyIpAddress(String ipAddress) {
return WHITELISTED_IPS.contains(ipAddress);
}

@Override
public void destroy() {}
}

Basic Authentication

It is also one of the simple ways to apply.

When to use: It fits designing APIs for 3rd parties services integration and limited access, not public access purpose, it is very similar to the API-KEY

How to use: Request Header Authorization BASIC mode ( username + password + Base64 algorithm encoding

How it works: One simple way is to create Servlet Filter Security and validation Authorization BASIC mode. So every user makes a request, the Filter Security will check and validate first before it continues into the API controller part

ex : curl 'http://localhost:8080/api/test' --user  'bill:abc123'  -H 'Connection: keep-alive' --compressed
final String authorization = request.getHeader("Authorization");
// Get encoded user and password, comes after "BASIC "
// Decode it, using any base 64 decoder
String authValue = new String(Base64.decodeBase64(authorization.substring("BASIC".length())));
String username = getClientUsername(authValue);
String secret = getClientPassword(authValue)
private String getClientUsername(final String authValue) {
String username = authValue;
final int endIndex = authValue.indexOf(':');
if (-1 < endIndex) {
username = authValue.substring(0, endIndex);
}
return username;
}

private String getClientPassword(final String authValue) {
String password = authValue;
final int beginIndex = authValue.indexOf(':');
if (-1 < beginIndex) {
password = authValue.substring(beginIndex + 1);
}
return password;
}

Oauth2

It is a standard and popular way to use.

When to use: When the users own their password with the context of the websites, e-commerce, mobile backend, and others.

How to use: username + password + access_token + expiration token

How it works: The main idea of the Oauth 2.0 standard is that after a user log-in into the system with their username and password successfully, the server will return back with the tokens (access token and a refresh token). Within the access token, The server can exchange with the current user login such as user-profile, roles, permission, and more.

oauth2

Oauth2 + JWT

When to use: It seems similar to the Oauth2 context above.

How to use: username + password + JSON map+ Base64 + private key + expiration date

How it works: When user login with username + password for the first time, the system will exchange back the access token, which this token represents a JSON map containing all user information, such as user profiles, roles, and privileges, encoded with Base64 and signed with a private key.

The main difference with Oauth2 is that we store user information state in the token, while services are stateless. This means that the server can decrypt the token into the user information state and there is no need for additional looking up from the database with this token. This is a huge benefit in reducing the load on the server.

This approach is widely used all over the world.

Bonus with other securities: OTP, 2FA

OTP (One time password): it can be used with SMS verification or the passcode algorithm

2FA (two-factor authentication): There are 2 steps :

1 — username + password — passed

2 —The second factor in authentication can be a smartphone, a personal item that almost everyone carries around with them on a daily basis. To use your smartphone for 2FA you will need to download and install an authenticator application. Here are some examples of supported applications:

These models above to make your system even more secure, and at the end they still exchange back as a token after successful authentication.

Conclusion

I hope this article can help you and enlarge your knowledge with the securities.

If this article is helpful, please support to press on the clap button and help other readers to reach this story as well.

I look forward to the feedback and comments.

--

--

sophea Mak
Javarevisited

15+ years of professional experience engineer software development with JAVA and open-source majority and DevOps lately. https://www.linkedin.com/in/sopheamak