API security has become a forefront issue for modern enterprises. However, there is a spectrum of API security implementations, and not all of them are effective. Too often, APIs only adopt HTTP Basic Authentication, API keys, or token-based authentication, overlooking a major concern: identity. To prevent vulnerabilities and reap efficiency benefits, a comprehensive identity focus is critical for fully-evolved APIs.
This is why we’ve created the API Security Maturity Model. Inspired by the Richardson Maturity Model, which outlines increasing degrees of web service development maturity, the API Security Maturity Model reframes the model within the context of security. Within this model, security and trust are improved the higher up you go.
- Level 0: API Keys and Basic Authentication
- Level 1: Token-Based Authentication
- Level 2: Token-Based Authorization
- Level 3: Centralized Trust Using Claims
The more evolved API security is, the more identity emphasis it tends to have. So, how do we encapsulate identity with APIs and make it useful? APIs that utilize OAuth and OpenID Connect can take advantage of Claims, an advanced form of trust. Tokens such as JWTs utilizing Subject and Context Attributes can delegate platform-wide trust.
More on the specifics of that below. But first, let’s expand on each maturity stage within the model to understand its benefits and drawbacks.
APIs at Level 0 use Basic Authentication or API keys to verify API calls. These are inserted within the header or body of the URL of the API request. This is the level of security that most APIs adopt. Most APIs established this authentication years ago, and unfortunately never evolved from there.
For example, consider an eCommerce store. It makes API calls to a payment API based on user purchases. It sends authentication in the form of an API Key or Basic Authentication in the header to the app and passes it to APIs. The user ID is placed in the Body or URL. In the example below, there are two APIs:
INVENTORY. Since HTTP Basic Authentication or API keys only authenticate the
STORE_WEB, the store must pass on user data.
You may be thinking: aren’t API keys sufficient? Well, this method is actually very basic, wrought with vulnerabilities. Not only are keys constantly compromised, but API key verification relies on machine-machine verification, not bound to the identity of the user at all. Lastly, this method only provides authentication, the act of proving an assertion, and does not cover authorization at all.
APIs at Level 1 utilize Access Tokens for authentication within a token-based architecture. Such Access Tokens delineate the type of user (machine, app, user, etc.). As this enables privileged access, it helps in environments where the separation of internal and external users is required. Level 1 provides better auditing since user identity is part of the request.
For example, consider token-based authentication at the eCommerce store. When we introduce a back office, the same problem occurs. Custom logic is needed to know if the request is a back-office request with elevated privileges or if it comes from the store web.
At Level 1, anyone with a token can modify the API, meaning privileged access can be hacked. Furthermore, Level 1 only covers authentication, not authorization. In other words, this strategy doesn’t ask what are you allowed to do? When only using tokens for authentication, all authorization becomes custom code. Thus, custom mechanisms like if statements must be coded. This is negated in Levels 2 and 3, where you can utilize token data for authorization, thus generalizing authorization logic.
APIs at Level 2 are more evolved, using token-based architecture for Authorization. Authorization delineates privileges for the requesting party, asking what are you allowed to do? APIs in Level 2 adopt OAuth, a widely adopted authorization standard in which client requests require an OAuth server for authorization. Maintained by IETF, OAuth 2.0 defines varying flows to obtain tokens, enabling the ability to grant access to resources without the need for a password.
One great benefit of OAuth is Scopes. Scopes can be utilized as “named permissions” within a token. These scopes can specify user privileges. OpenID Connect defines standard scopes that can be used to generate standard identity arguments. Or, you can create custom scopes) for your API. Scopes have more useful data and are better than building if statements into a system.
Let’s consider our eCommerce store again. Now, we introduce Scopes, so that the public web store and back-office can have different privileges. However, some operations overlap. The Scope
LIST is used to list invoices in the billing API. The
ID to list for is in the URL or passed as a request parameter. Thus, it’s possible to manipulate the call to list invoices for another user. Thus, the Scope is not sufficient. Scopes also lock down what the client application is allowed to do; they don’t help with the particular user since they are only “names” and not “values.” Instead, Claims should be used so that the parameter is baked into the token. Then it’s easy to separate back office privileges from
One problem in Level 2 is that the system faces the threat of being decompiled. When identity is built directly into the API, logic errors may be discovered and exploited. Level 2 also introduces a higher degree of system complexity, as some API request parameters may rely on other API responses or other conditions. What happens when one API calls another API that fails? Or, what if the data request is full of errors? You can’t always assume the data passed from one API to the next is always correct. These realities cause cascading issues of trust, easily becoming an intertangled mess. We call this a “spaghetti of trust.”
The final tier, Level 3, is the most evolved API security platform. This practice involves centralized trust with Claims and possibly signed JSON Web Tokens (JWTs). In doing so, we solve all the problems outlined above.
What are JWTs? Well, to clarify common misconceptions, a JWT is NOT a protocol. It is a signed piece of data. OAuth flows utilize JWTs to verify transactions. JWTs can be used to share Scopes.
And Claims? Claims are essentially assertions. For example, consider a written statement: “Jacob is an identity specialist, says Travis.” This claim has a Subject (Jacob), an Attribute (that he is an identity specialist), and an Asserting Party (Travis). If you trust Travis, then you trust the Claim. Many Attributes can make up identity. There are Subject attributes, like name, age, height, weight, etc. For these attributes, the Asserting Party would be the police or tax authorities. There are also Context Attributes, such as the situation, timing, location, weather, etc.
Instead of trusting the attributes themselves, it is far better to trust claims made by common parties. Identity systems use Claims with similar anatomy for verification. If you trust the OAuth Server that issues keys, then you trust the claim being made. To verify a claim (simplified):
- Requesting Party calls the Issuer.
- Issuer returns data, signed with a private key
- Requesting Party sends to another party
- Replying Party verifies the signature with a public key
This method solves the issue of trust, by trusting the issuer of tokens rather than the claims themselves.
In cybersecurity, it’s rarely encouraged to invent your own traffic rules. For centralized trust to function, authorization systems require the use of stable protocols. Just as street traffic follows common protocols, identity systems require their own shared open standards. These protocols are OAuth and OpenID Connect. Utilizing these standards, an app can share secure, asserted data within JWTs for verification.
Trust is a subjective thing. In designing a secure API-based system, should we trust keys, tokens, passwords, machines, or users themselves? The answer is more complex than most API designers think, and maybe pivotal to safeguarding your platform as a whole.
As the API Security Maturity Model displays, highly mature APIs place trust in very few sources. Especially, these evolved APIs place trust in the issuer of tokens. This does not guarantee the truth but is the closest representation to validating the identity of requesting parties. Furthermore, standardizing this process with centralized trust removes spaghetti code and wasted effort on custom code.
Essentially, the pinnacle of API security is to trust claims, not attributes. When building an identity-based API security system based on claims, remember some best practices:
- Organize sensitive data only to be reachable by OIDC server
- Include identity data in token, not context attributes
- Opaque tokens for the public, JWTs internally
- Limit data exposure only to when the client needs it
- Avoid app-specific rules
Without more advanced security, APIs could easily be made vulnerable with a rogue key left in a Github repository. Thus, API providers must make smarter security decisions that safeguard the integrity of the entire platform.