API Security Best Practices
On this page
Introduction
With the rising threat of cyberattacks, securing APIs has become business-critical. Especially as many security reports indicate that web APIs are quite vulnerable. Thankfully, by following a few best practices, API providers can ward off many potential vulnerabilities. Below, we cover top API security best practices, which are good things to keep in mind when designing and creating APIs.
1. Always Use a Gateway
Our first recommendation is to always put your API behind a gateway. API gateways centralize traffic features and apply them to every request that hits your API. These features may be security-related, like rate limiting, blocking malicious clients, and proper logging. Or, they may be more practical and business-related, like path and headers rewriting, gathering business metrics, and so on.
Not having these controls could easily result in a serious security threat. Without a gateway, API providers would have to reinforce each endpoint with these features one-by-one. An API gateway eases the process of adding or fixing these features. Thankfully, there are plenty of API gateway products available on the market.
2. Always Use a Central OAuth Server
Next, do not let your APIs or gateways issue access or refresh tokens. A centralized OAuth server should always issue such tokens. Issuing tokens requires many complex processes: authenticating the client, authenticating the user, authorizing the client, signing the tokens, and other operations. All these functions require access to different data, such as client information or the preferred authentication mechanism. Furthermore, if many entities issue and sign tokens, it becomes increasingly challenging to manage all the credentials used for signing. Only one entity can safely handle these processes — an OAuth server.
3. Only Use JSON Web Tokens Internally
When APIs are concerned, using JSON Web Tokens (JWTs) as access and refresh tokens is a good practice. Services that receive JWTs can leverage claim information to make informed business decisions: Is the caller allowed to access this resource? What data can the caller retrieve?
However, when tokens are exposed outside your infrastructure and especially when exposed to third-party clients, you should use opaque tokens instead of JWTs. Information in a JWT is easy to decode and thus available to everyone. If JWT data is public, privacy becomes a concern. You must ensure that no sensitive data ends up in the JWT's claims. What is more, if you share JWTs with third-party clients, chances are that they will start depending on the data in the JWT. It might become a liability, even if the data is not sensitive. Once integrators start depending on the contents of a JWT, changing the token's claims could result in a breaking change, requiring costly implementation upgrades in all third-party clients.
If you want to use opaque tokens externally but also benefit from JWTs in your internal communication, you can use one of two approaches: the phantom token approach or the split token approach. Both involve an API gateway in the process of translating an opaque token into a JWT.
4. Use Token Exchange When Sharing Tokens
Whenever a service calls another service, you need to consider the access token to send to the upstream service. In small setups with only a couple of APIs you might forward the original access token. However, this solution does not scale well, since reusing the same token gives the receiving service all permissions that are associated with the access token.
Instead, use the token exchange flow to obtain tokens that contain enough information for the receiving service to authorize the request, but do not allow it to reuse the token elsewhere. This is especially important when requests cross security boundaries, like when you call services external to your organization.
When using token exchange, never allow services to handle the same token that the client sends to the API. For example, use the phantom token approach described earlier. Otherwise, your services will be able to call your publicly-facing API endpoints creating a vector for abuse. You can also utilize other token sharing techniques, like embedded tokens to achieve similar results.
5. Use Scopes for Coarse-Grained Access Control
OAuth scopes limit the capabilities of an access token. If stolen client credentials have limited scopes, an attacker will have much less power. Therefore, you should always issue tokens with limited capabilities. Verification of token scopes can be done at the API gateway to limit the malicious traffic reaching your API. You should use scopes during coarse-grained access control. This control could include checking whether a request with a given access token can query a given resource or verifying the client can use a given Content-Type.
6. Use Claims for Fine-Grained Access Control at the API Level
You should always implement fine-grained access control at the API level. This access control complements any control done at the API gateway level, and should be architected so that even if a malicious request slips through the gateway, the API will still reject it. This practice safeguards against situations in which attackers bypass the gateway.
A fine-grained access control focuses on securing an API from a business perspective. The API should verify whether the request can reach the given endpoint. It should also check whether the caller has rights to the data and what information can be returned based on the caller's identity (both for the client and user). The 2019 OWASP Top 10 API Security Vulnerabilities lists broken object level authorization (BOLA) as the top API vulnerability, so it's worth remembering this one.
7. Trust No One
Zero-trust is not just a buzzword — your API should limit trust to incoming traffic. Period. One of the steps toward building zero-trust is using HTTPS for all API traffic. If possible, use HTTPS internally so that traffic between services cannot be sniffed.
Your services should always verify incoming JWTs, even if they are transformed from an opaque token by the gateway. This again helps to mitigate situations where a request manages to bypass your gateway, preventing a malicious actor from operating inside your company or infrastructure.
Zero-trust also means that your services should deny access by default. Then use claims-based access control to allow access to requests that fulfill concrete access control policies.
8. Create or Reuse Libraries for JWT Validation
Proper JWT validation is crucial for the security of your APIs. Yet, if every team implements their own JWT validation solution, you risk increasing overall system vulnerability. Mistakes are more common, and it's difficult to fix bugs.
Instead, create a company-wide solution for JWT validation, preferably based on libraries available on the market and tailored to your API's needs. Standardizing a company-wide JWT validation process will help guarantee the same level of security across all your endpoints. When issues arise, teams can resolve them more quickly. For security-sensitive tasks like JWT validation, quick threat resolution is incredibly important.
9. Do Not Mix Authentication Methods
Do not mix authentication methods for the same resources. Authentication methods can have different security levels. For example, consider Basic authentication versus multi-factor authentication. If you have a resource secured with a higher level of trust, like a JWT with limited scopes, but allow access with a lower level of trust, this can lead to API abuse. In some cases, this could be a significant security risk.
10. Protect All APIs
Do not leave any of your APIs unprotected. Even internal APIs should have protections implemented. This way, you're sure that the API is protected from any threat from inside your organization.
APIs are commonly created for internal use only and made available to the public later on. In such scenarios, proper API security tends to be overlooked. When published externally, the API becomes vulnerable to attacks.
Remember that security by obscurity is not recommended. Just because you create a complicated name for an endpoint or use an obscure Content-Type does not mean the API will be secure. It's only a matter of time before someone finds the endpoint and abuses it.
11. Issue JWTs for Internal Clients Inside Your Network
If you have internal clients operating only inside your network, you can have your OAuth server issue JWTs for such clients instead of opaque tokens. This will avoid unnecessary token translations. However, you should only apply this strategy if the JWTs do not leave your network. If you have external clients, or if the tokens are used externally, you should hide them behind an opaque token, as noted before.
12. Use JSON Web Key Sets for Key Distribution
To verify a JWT's integrity, an API must access a public key (if the JWT is asymmetrically signed, as recommended). You can accomplish this in a couple of ways: you can hardcode the key's value or query some endpoint at your service startup and cache the result.
The recommended method is to obtain a key from a JWKS endpoint exposed by the OAuth server. The API should cache the downloaded key to limit unnecessary traffic but should query the JWKS endpoint again whenever it finds a signing key it doesn't know.
This allows for a simple key rotation, which the OAuth server can handle on-demand without impeding the API services. Using key sets instead of keys also allows a seamless key rotation for the clients. The OAuth server can begin issuing new tokens signed with a new key but existing tokens will remain valid as long as the old public key is part of the key set.
13. Always Audit
Maintaining high standards for your APIs, both from a security and design point of view, is not a trivial task. Therefore, consider splitting responsibility between different groups of people and having other teams audit your APIs.
There are different approaches to setting up governance over your API. You could have a dedicated team of API experts review the design and security aspects, or create a guild of API experts picked from different groups to offer guidance. However you organize governance, ensure you always have additional eyes checking your APIs.
14. Manage Claims Centrally
As defined by the JWT specification, a claim is a piece of information asserted about a subject. It's good practice to have these claims asserted by a centralized OAuth server — this makes it easier to control which claims appear in your tokens. This is important for privacy and security reasons.
Whether calling internal or external services, all APIs should only use claims asserted by the centralized server and should not add additional information nor issue tokens. Managing claims centrally allows you to control the information flowing between the APIs to ensure they do not leak excess data.
15. Abuse Doesn't Have to Be a Breach
Just because your API security isn't breached doesn't mean that everything is fine. You should gather metrics and log usage of your API to catch any unwanted behavior. Watch out for requests iterating over your IDs, requests with unexpected headers or data, customers creating many clients to circumvent rate limits, and other suspicious cues. Losing data due to API abuse can be just as harmful to your business as a hacker breaking through the security.
16. Keep Your Tokens Secure
Although not concerning APIs directly, an important part of a secure API is how securely access tokens are handled by clients. If access tokens can easily be stolen, they can then be used to steal data from an API. Mobile and backend clients can store those tokens pretty securely, but it is not the case with browser-based applications. Single Page Applications developers often wonder how to securely keep tokens in the browser, which should be treated as a hostile environment. The OAuth for Browser-Based Apps specification currently recommends keeping the tokens out of the browser altogether. This can be achieved by introducing lightweight backend components that are capable of safeguarding the tokens and issuing secure cookie-based sessions. The pattern is known as backend-for-frontend or token handler.
Conclusion
Securing an API with high-standard security is a paramount concern. As seen above, there are many technical strategies to consider when designing your authorization processes, which, if undermined, can directly affect API security. A stronger foundation is only made possible with a secure, centralized OAuth server responsible for token issuance and claims assertion. Many suggestions also revolve around treating internal APIs with the same care as public-facing endpoints. By following these protective measures, you can sufficiently safeguard APIs and thwart unwanted behavior.
Michał Trojanowski
Product Marketing Engineer at Curity
Join our Newsletter
Get the latest on identity management, API Security and authentication straight to your inbox.
Start Free Trial
Try the Curity Identity Server for Free. Get up and running in 10 minutes.
Start Free Trial