Short digression first. I’m currently sitting in a lovely and almost on-time train from Wrocław to Kraków in order to get to Devoxx PL conference and rant again about, guess what, web API. I was assessing which part of the design should I write about today, put on the headphones and kicked my usual collection of writing music mainly composed of video games soundtracks. The first track that pops up randomly is “Diablo II – The Wilderness”. It’s dangerous out there, I thought, API security it is then.
Welcome to the next chapter of our journey through the vast realms of web API. We have wandered far away from home now, and the wilderness is getting scary and dangerous. Let’s see what we can do to stay safe here.
It’s not the first security focused article here, but probably the most technology agnostic. In episode 63 we tackled some hands-on examples of security aspects in Spring framework. In episode 77 we looked at details of AWS IAM – Identity and Access Management while following in episode 78 with other AWS services related to security.
Today we will talk about authentication and authorization ways in web APIs, keys, JWT, OAuth, TSL, throttling, threats and attacks and explore some general tips on how to give bad guys hard time.
The simplest authentication mechanism is lack of thereof. Some APIs are open and accept any requests, but if we want to go into any serious business, we need to think about a way of verifying and controlling who’s calling us. So the simplest real mechanism is basic authentication – we are getting an Authorization header with value Basic followed by username and password, base64 encoded. This approach requires secure TSL connection, as it basically transmits credentials in plain text and is generally not the best idea, we can do better.
Randomly generated long tokens are better than human-created passwords as they are impossible to guess, as opposed to “admin1” or “qwerty”. The simplest mechanism here is to provide such token to API user via a secure channel and then expect it to be present in every request in Authorization or custom header. It’s not a good practice to transmit tokens as URL parameter, since despite using transit protection in the form of TSL, there is a risk that the URL might land where it was not supposed to land, for example in server logs, which is a risk. With HTTP header, it is less likely. Simple token is not much better than long, random password though so we can do better.
JSON Web Tokens is an open standard described in RFC 7519 that gives the token a structure and includes a number of claims in it, thus serving as an authorization mechanism. The token is composed of a header, payload, and signature, all base64 encoded and separated with dots. The header indicates which hashing algorithm we use to sign the token. The payload may contain standard claims like expiration date or issuer and custom claims that may be any information that parties want to share, like username, type or a set of privileges. The third part is a hash of two first parts and a secret. All of that is then thrown into Authorization header with a Bearer prefix. Looks decent, but what about the mechanism for issuing such tokens? Let’s move on to that.
OAuth Is an open standard described in RFC 6749 for access delegation, meaning that the body issuing tokens that allow access to services might be separated from those services. The commonly used version is 2.0 released in 2012. Parties taking part in OAuth flow include Resource owner, Client, Authorization server and Resource server. There are several grant types: Authorization code, Implicit, Resource owner password credentials and Client credential which is most suitable to be used in web APIs. The general idea is that Client obtains a secret from the Resource owner, then uses those to call Authorization Server to obtain an access token to finally call the Resource server with that token to get the actual stuff done. Token has a certain lifespan and can be refreshed to prolong it or revoked to shorten it.
Aside from authentication and authorization our users, it’s good to have a graceful mechanism to deal with increased traffic we are unable to process at the moment or with a client that surpasses its request quota. We could, of course, return HTTP 500 Server Error and leave it at that, however, we may be more specific, and tell our clients when the request should be attempted again. In that case, we can return HTTP 429 Too Many Request and three special headers. X-Rate-Limit-Limit says how many requests we can handle in a single time window, X-Rate-Limit-Remaining indicates how many are left in the current window and X-Rate-Limit-Reset tells us when the new window starts. This way if the client is out of limit, it knows that there no point in retrying until the new window opens, thus saving resources on both ends.
- Validate inputs – well this should go without saying. Use strong typing whenever possible.
- Validate Content Type – don’t trust declaration of content type, validate whether the actual content is what the header says it is.
- Use HTTPS – even if you have secure authentication and authorization mechanisms, its good to keep stuff private.
- Don’t redirect HTTP to HTTPS – it might seem convenient, but the redirection call might be intercepted and the Location maliciously altered without the user realizing. HTTP call should be clearly stated as an error so that everyone is on the same page.
- Do not expose internals – don’t put the name of your server in URL or stack traces in your error objects, that’s a piece of information that can be used against you.
- Don’t invent your own cryptography – cryptography is hard. If you have a Ph.D. in it and 10 years of experience… you probably still shouldn’t. Don’t confuse hiding technology with security by obscurity. Cryptography should rely on well-known and relentlessly tested algorithms combined with secrecy and strength of keys.
- Think about bot protection – that’s one of many things you can delegate to an API management platform or tool instead of developing it on your own. There are many such services and you can find more details in episode 47.
- Check owasp.org – maintains a list of most common security problems in web applications and how to deal with them.
Security is a vast topic, but we need to stop somewhere. In the next episode, we will continue our journey through the API world with different topics. Stay safe!