Your APIs Are Only as Secure as Your JWTs
API security has matured over the years. As an industry, we have moved from API-key-based security to token authorization. And most companies today typically leverage either OAuth or OpenID Connect standards to secure their APIs with tokens. This is a positive trend (which large API ecosystems can take even further) that helps increase the security of APIs.
Since Access tokens are short-lived, they don't share the risks of immutable API keys. They also include (or reference) all the necessary data that an API needs to perform authorization decisions. As with all security solutions, though, there are some things to look out for when securing APIs with access tokens, especially when JSON Web Tokens (JWTs) are considered.
The Popularity of JWTs
JWT has become a very popular format for access tokens, mainly thanks to the ease of use and statelessness. However, it's not the only access token format — by-reference and opaque tokens are also widely used. You can even combine formats in a pattern called the Phantom Token Approach
JWTs can be used outside of access tokens too. A JWT is useful in many scenarios that require sharing integrity-protected data (thanks to the token's signing) or confidential data (using encrypted JWTs). For example, JWTs can authenticate applications, where an application issues a signed JWT, and the receiver can verify the app's identity by verifying the signature.
There is nothing wrong with using JWTs as access tokens, though the choice is sometimes made hastily, without proper insight. Often, developers decide to use JWTs simply because it's a trendy security feature. Or, what's worse is that some folks claim JWTs are a great way to manage sessions across backend instances without a persistence layer (thanks to the fact that all the session data is kept inside the JWT).
One should remember that JWTs were never designed for handling sessions. They're not a good fit for this for one simple reason: JWTs can't be invalidated. They are valid for as long as the expiration time of the JWT itself. This means that if you're using JWTs for session handling, your users can't log out of your application. Instead, they must wait until their session is over.
The popularity of JWTs adds to these problematic usage patterns. Since they are popular, developers don't always consider the security implications of their use. A developer will often finish a tutorial on how to issue and verify JWTs in framework X and implement it straight away in their solution. But without security forethought, hasty adoption can lead to misconfigurations or improper settings that render JWTs much less secure.
The Devil Is in the Details
As with many technologies, especially around security, the details are essential. It's simple to start using JWTs, but it's important to use them securely — this includes understanding why you're using specific settings and how to use particular aspects effectively.
jwt is one of my favorite tags on Stack Overflow, and I tend to answer and review many questions here. Frequently, I encounter people using JWTs with symmetric signing algorithms. Even though asymmetric signing is usually preferred, you should be aware of the caveats and recommendations when using symmetric signing.
For example, are you using a secret with enough entropy so that it's hard to brute-force it? Do you have a procedure in place for key rotation? If many services are verifying your JWT, they all need access to the secret. Thus, you need to be able to quickly feed them the new secret. Not to mention that the secret cannot be shared with a Single Page Application (SPA) or a mobile app as it will not be a secret anymore (anyone can access the code of your SPA and mobile app, so they can easily extract any embedded secrets).
Think about it this way — if you organized a concert, you wouldn't print tickets on a home printer, would you? Issuing tickets to a concert doesn't guarantee they won't be abused — it's how you issue and secure those tickets that protects them.
Using misconfigured JWTs or JWTs that misuse important features (such as using weak symmetric keys, or allowing the algorithm 'none' to be used for verification) can lead to Broken Object Level Authorization or Broken Function Level Authorization vulnerabilities. These vulnerabilities can expose a state where the user can access other data or endpoints that are beyond their privileges. Together with Security Misconfiguration, these vulnerabilities are all present on OWASP's list of top ten API vulnerabilities.
Proper usage of JWTs is truly an integral part of your API's security layer. This is also the case when the content of the JWT is insufficient for an API to perform an informed authorization decision. This means that tokens should be populated with proper scopes and claims depending on the requesting client, authenticated user, method of authentication used, and other factors.
Security Is Hard
It's common knowledge that security is hard. It's even harder when you implement it in a homegrown way. It's also impossible for all developers in your organization to become security experts, and thankfully, they shouldn't have to.
When considering security, as in many other areas of software development, it is good to tap into the knowledge and experience of others. For many areas of security, you can find best practices and cheat sheets. Some are even published as standards (or draft standards), like the security best practices for OAuth or the OAuth best practice for native apps. Creating secure solutions is much simpler once you follow those best practices.
When it comes to JWTs, at Curity, we have compiled a best practice article to help others avoid the common pitfalls. There is also a free online course on the same topic.
You should never implement new technologies just because they're hip or many people around you recommend them. This is especially true when it comes to security features. Your decisions should always be informed — you should be aware of why you want to use that particular piece of technology, and, even more importantly, how to properly integrate it.
Thankfully, there are many resources out there, as security experts are keen to share their experience so that others can create more secure and robust solutions. Don't hesitate to reach for those resources!
Sometimes adhering to security best practices is more straightforward when using the right tools. For example, if your authorization server doesn't allow unsigned JWTs, you don't have to worry about rejecting unsigned JWTs. The Curity Identity Server comes with a feature-rich Token Service to facilitate working with JWTs, claims, scopes, impersonation techniques, token sharing, and many more.