Hardcoded secrets, unverified tokens, and other common JWT mistakes
r2c.dev
r2c.dev
From the Wireguard paper [0]:
> 'Finally, WireGuard is cryptographically opinionated. It intentionally lacks cipher and protocol agility. If holes are found in the underlying primitives, all endpoints will be required to update. As shown by the continuing torrent of SSL/TLS vulnerabilities, cipher agility increases complexity monumentally.'
It would probably save a lot people headaches if jwt.io published a chart of "use case" and "algo/cipher selection". From what I've researched, all the JWT code libraries give you a menu of selections, and it's on you to research which algo/cipher to use, and given the volume of selections, that's a substantial reading list.
Is there a 'versioned' JWT that picks sane defaults as they are developed + improved?
When it comes down to it, you need some crypto knowledge to make these choices. We constrain them to PS256 and ES256 in our system (but then you also need to make sure that the curve used for your keys for the ES256 signature is an acceptable one)
“ This is still in early development. It has not been reviewed in a security audit yet, so please be aware that it is not expected to be ready for use in production systems.”
https://github.com/paragonie/paseto
And has had a lot more attention.
And yes I just spent 2 hours writing that.
There can't be a great authentication/crypto that can improve over time. In theory, it must block old things to be state of the art. In practice, it's used in client-server communications, that would break all communications if nothing could talk unless they're always on the exact same version.
https://connect2id.com/blog/nimbus-jose-jwt-6
would give some reasonable idea on the practical difference on modern hardware.
For JWT, it's essentially promises of easy authentication process (rarely talking about the hard things like logout) spread like landmines over Medium and blogs, waiting to blow your foot and all your application with it.
My boss wrote up this article on different ways to revoke JWTs: https://fusionauth.io/learn/expert-advice/tokens/revoking-jw... which examines a few interesting methods.
- Have a refresh token which is only usable with the (right) auth server and can only be used to generate new access tokens. As it's only usable with the auth server it's easy to revoke (it's kinda like a session cookie).
- Keep validity of access tokens short.
So by revoking the refresh token the logout will be done in at most highest_refresh_token_start_time + refresh_token_validity.
Through sadly any "faster" logout method on a distributed system is indeed quite complex. (Like propagating bloom filter headed blacklists of early revoked access tokens).
Why? I understand it might be acceptable for the next "Uber for Cats" SaaS but if you protect an account that is actually important, not being able to revoke an access key is an issue.
> Keep validity of access tokens short.
But then you are actually spamming your authentication service and it even starts to blur the distinction between access token and refresh token.
I understand that there is many specific use cases where JWT makes sense like when you have to delegate authentication but for other case which shouldn't be recommending it, not being able to properly logout is not a security practice we should have in 2020 and later.
OpenID Connect with JWT handles logout just fine if you really want it. Call the /oidc/tokeninfo to check the token is valid instead of verifying the signature offline. Of course this doesn't have the benefits of a decentralized token anymore. Can't have the cake and eat it too.
Why hard?
You can always black list them
I tend to believe that it could be used by gmail, but it's just a guess
If you don't read the docs, don't have the right mentoring, etc etc, your first go at anything is likely to be of low quality. Calibrate your confidence better, especially for anything security-related. Don't put it into production if you've only skimmed the docs (the mistakes we're talking about are novice mistakes).
Another conclusion you could draw is "use a built in security library not raw JWTs". There's just as much reason not to use a security library as there is not to use a SQL ORM.
That goes away.
You're welcome to disagree but don't gloss over the fundamental point being made.
In other words "the function to verify the user token" should also return the payload data (e.g. the user ID, or whatever), and it should ideally be the only way to get that payload data. That way there's very little that can go wrong:
- If the verification function doesn't get called, then the application doesn't get its payload data (e.g. user ID), and hence can't be misusing that data.
- If the application is using the payload data, then it must have come from the verification function, and hence has been validated.
I'm adding JWTs to a system right now, and forcing this interface by encrypting all of the payloads. The only system with access to the decryption key will refuse to decrypt tokens which aren't signed, valid, non-expired, etc.
Maybe I'm talking my book (because I work for a company that sells software which generates JWTs) but I think it isn't that hard to securely validate them. It's just matching on JSON keys, mostly, and can be wrapped up in a library (like https://github.com/jwt/ruby-jwt).
I also wrote an article on how to build secure JWTs: https://fusionauth.io/learn/expert-advice/tokens/building-a-...
I guess the question is, where's the balance between the responsibility of the application developer vs the RFC authors? That's an interesting discussion to have, because I bet there is at least one valid use case for every one of the JWT RFC sections. (I've even heard of valid cases for the "none" algo, like if you are very concerned about performance and have other ways of validating your clients, such as mutual TLS certs.)
I've never written an RFC/standard, but I bet it isn't an easy task.
In a JavaScript browser app without backend server it is impossible to use it safe. The token must be visible to JavaScript which is unsafe by default.
You also need to store both the token and refresh token in your app which makes the refresh safety feature useless.
I still don't get JWT. It solves requesting an auth server all the time but when the token is stolen it can be used until it expires. This can be solved by setting the expiration very low but then the auth server is still requested all the time.
was even escaping input viable strategy?
if you escape thing, then your data in db is broken
parametrization seems like the only strategy out of those 2
switch(int)
case 1: age
case 2: salary
There is a RFC which also details the best practices for JWT: - JSON Web Token Best Current Practices: https://tools.ietf.org/html/rfc8725
On the similar topic, some more interesting RFCs / Drafts from IETF on OAuth: - OAuth 2.0 Threat Model and Security Considerations https://tools.ietf.org/html/rfc6819
- OAuth 2.0 for Browser-Based Apps - https://tools.ietf.org/html/draft-ietf-oauth-browser-based-a...
Oh yes, now defunct but we had a search engine just for that :| http://archive.is/0GNLl
I plan on going for a simple bespoke solution which just uses an off-the-shelf algorithm to sign a structured payload (likely BARE), which in addition to being less full of shit will make for smaller and more managable tokens.
> ... full of footguns that it was unacceptable for a security-critical component.
What it not secure about it?
1. It seems any piece of content talking about Oauth 2 is referring to 3rd party, external authentication. Ie i want a client API to talk to my API but with an identity managed by a third party entirely. This is not our use case, but seems to be the primary focus for Oauth 2 articles.
2. The rest of the spec _feels_ very loose for our use case. There's loose definitions _(it feels like)_ on some basic patterns of tokens and renewal tokens, but it all seems so wildly flexible that we're almost lost on what to actually write, what technologies to use to generate tokens, etc.
Any tips for narrowing the field, to implement the right thing, in the right way? This article struck a chord with me.
Take a look at https://spiffe.io/ which avoids these concerns focuses specifically for system to system authentication at scale.
first off: the whole idea of using oauth in your systems is to externalize authentication. its main usecase is if you have several services which share the same users... usually microservices, but everything else which supports oauth works as well, obviously
if you do not actually want to externalize your authentication, then oauth is likely not what you actually want. (keep in mind this only applies to the process of authentication. you will probably still create a `users` table to put a foreign key on your domain entities. said users table just won't have a `password` field or similar)
under this perspective it makes sense that you basically only find blog posts to integrate 3rd party tools, right? you're just setting out to implement a new oauth2 server otherwise after all, and... i'd strongly suggest you don't. there are so many pitfalls you can fall into. its not a trivial subject.
thankfully, there are readily available FOSS oauth2 servers like keycloak around if thats what you want to use...
but to come back to my initial point: if you do not want to externalize your authentication, then do not use oauth! almost all frameworks already have tools available you can use to authenticate with api tokens. it keeps you within the bounds of your frameworks strength and you can automatically fetch a token on your frontend after the user authenticated with your usual flow.
but i'm already off topic considering your first question. if you do use a solution like keycloak, there are `oauth clients`. these behave exactly as you want.
We don't want to externalize it. For clarity _(because i think i implied internal API->API)_, our primary concern right now is a client API interfacing with our API. We want to choose some implementation that clients would expect, and easily reason about.
There may be a future where those clients have to authenticate to multiple APIs / microservices of ours, but currently it is one API setup for this explicit purpose.
> but to come back to my initial point: if you do not want to externalize your authentication, then do not use oauth! almost all frameworks already have tools available you can use to authenticate with api tokens.
Yea, not using Oauth was my thought was well. My concern however, was trying to pick something clients would expect. I don't want it to feel custom, arbitrary, hodgepodge.
Our very early impression / plan is to use a token renewal, and a shortlived token, similar to oauth. However this loose, and custom, and i don't want clients feeling like we're making things up. Hell, i don't want to make things up.
My current thought is that I need to research JWT more, as it may fit what we need, and be more standardized.
edit: And PASETO
Just start a keycloak server with docker and write a small Webservice in your language of choice which only let authenticated users open the website, which just prints the user name for example.
After you did that, you could write a second service which accesses that api using a client (not user) and prints which users have accessed it since it was started.
That should be doable within a few hours at most and give you a feel for the technology.
If you're fluent in python, I'd personally suggest just starting a hello world flask project, connect it to a keycloak and write a second cli script which simulates the non-user access. (If you use Java, keep in mind that springboot2 uses Springsecurity5, which was incompatible with the official keycloak Java sdk the last time I checked. Either use springboot1 or expect to have a slightly harder time figuring out what to write in the application.properties)
Reimplementing advanced authentication systems like jwt on your own is extremely error prone and frankly unnecessary if you're not in the business of authenticating users. I'd suggest to either use whatever your framework prefers (which is usually just a static and very long, manually rotated token) or try to externalize it by using readily available foss solutions
I think clients often expect one of the following (assuming everything is done over HTTPS): 1. HTTP basic auth 2. Login to the API, get a token, and use that token for requests 3. Generate an API key for the client and the client sends the key on every request 4. (Occasionally) Cryptographic signing of every request, sending the signature with the request
1 and 3 (if implemented as a username/password on the back end) are going to have similar trade offs. You are fully authenticating every request that comes in against your credentials data store.
Option 2 is where OIDC/OAuth2 comes into play or you could use a standard session token approach if you want. In OAuth terminology the client credentials grant is the simple flow where the client logs in with a username/password, gets a token back, and includes that token in API calls. Option 2 is also valuable if you want to support mobile clients with refresh tokens and such using things like the authorization code grant. Essentially allowing users to login to a mobile app once but use short-lived tokens when calling the API without prompting users for credentials every few minutes.
Option 4 can be an alternative to token-based approaches to reduce load on your credentials database but gets more complicated for the client.
Ultimately if you are looking to keep things simple I would lean towards HTTP Basic auth until/unless you are building a mobile app or otherwise really need to move to a token-oriented approach. At that point I would consider full fledged OIDC/OAuth2 or maybe a login that generates an API session token.
I still decided to go with JWT, since PASETO isn't yet standard and its software hasn't been around for long, but it definitely helped me understand more about the choice I was making. In particular, PASETO distinguishes between "local" tokens (producer and consumer can both use the same symmetric key) versus "public" tokens (producer and consumer can't use the same key, e.g. third party services run by different organisations; hence asymmetric keys are needed). PASETO encourages local tokens to be used when possible, since their keys can be shorter, they generate shorter tokens, they're faster to encrypt/decrypt, they're easier to rotate, etc.; public tokens are essentially a last resort when we must interact with a third party.
I was originally considering 'public' JWTs (i.e. asymmetric keys) for my use case, since we're using AWS API Gateway and that has the option to check the signatures of 'public' JWTs, and reject invalid ones before they ever hit my code. However, I eventually went with 'local' JWTs (i.e. symmetric keys), since the convenience of having AWS discard some tokens for us wasn't worth the (mostly logistical) overhead of using asymmetric keys.
My choice to use the JJWT library was also partly due to its recommendation by the JPaseto library ( https://github.com/paseto-toolkit/jpaseto ).
Since my primary concern is external client APIs communicating with ours securely, but also in a way that's not too-foreign. I imagine JWT would fit the bill of being widely known, but PASETO sounds really interesting too - having less things to get wrong sounds amazing. My concern with PASETO is that client APIs are huge slow customers, so i'm hesitant to choose anything that's not super mainstream.
I'll research PASETO more, but i feel like my decision would come down to fitting to what the customer can use - less change is more, in that case. Ie, JWT.
The easiest flow IMO: The API client needs a service account (username and password like a human). It authenticates to the OIDC server with the username/password and obtains a token. Then it can call any API service with the token.
It's quite straightforward really.
Eg we'd been using the `iss` field, a claim, to determine which key to use, when we should've used the `kid` in the header. All claims are now completely inaccessible until verified to the point where there's no way to access them short of manually deserializing the jwt. We'd also allowed some invalid tokens where the `exp` was in the past, only if you were refreshing the token of an active session. This could've caused problems if we ever wanted to issue a token that expired very quickly, since you could have a token that's theoretically 10-times longer lived than you intended.
1) One system produces a JWT which makes a bunch of "claims" about the bearer; e.g. 'their email address is alice@example.org'.
2) Another system which trusts the first system can consume these tokens and use the "claims" to authenticate the bearer, e.g. 'the email address alice@example.org is associated with user ID 12345, so the bearer must be user 12345'.
3) Now that the user is authenticated, the second system can initiate a session for them (e.g. storing a cookie in their browser, or whatever).
4) From now on that JWT should be rejected; we can approximate this by making it short-lived, but we can do better by the second system update some state. For example, updating the "last seen" time of a user, and rejecting any tokens issued before the this time.
The idea of 'using stateless JWTs for authentication' is presumably the common practice of avoiding steps 3 and 4: rather than having the second system initiate a session for the user, the JWT is treated as if it were a session token; rather than invalidating the JWT after its first use, it is accepted over and over again as part of the user's subsequent requests. This also requires the JWTs to have a longer expiry time than necessary, since they have to survive until the user's last interaction, rather than their first (in fact 'refresh tokens' can be used as well, but the principle is the same).
As for why it's a bad idea, I recently came across PASETO (a simplified alternative to JWT) which specifically calls this out as an insecure practice due to the possibility of replay attacks ( e.g. https://github.com/paragonie/paseto/tree/master/docs#was-sta... )
Do we use certificates for authentication? Yes, and it works well.
Do we use certificates for authorization? Not likely. Instead, usually we ensure ID of entity which we communicate with (as result of authentication) and lookup it's permissions in database.
Do we recognize any certificate body as immediate permission requisite? No! Instead, we have signed claim with public key which entity should present in order to prove it's identity (and prove possession of private key later). I. e. we issue end-entity certificate which describes and verifies public key of that entity.
Do we NEED to revoke these end-entity certificates or have them expiring really fast for normal operations like logout? No. There may be something similar to CRL and OCSP for handling emergencies, but this is optional. Instead, we are not trying to handle authorization tasks with authentication mechanism. It's different things. We may just change status for authenticated entity in authorization database. Like: "user:admin;device:XXXXX;status:terminated".
What semantical meaning has revocation/expiration of token issued to alice@example.org? She is no longer alice? I doubt that. She is no longer allowed here? Or her specific device is no longer allowed here? Or her service subscription is just expired? Either way it's not a question for part which recognizes known entities and must work in a "yes" or "no" fashion.
It's not wrong to use JWT for authentication. It's wrong to use ANY authentication for ANY authorization.
I feel like the problem is generally ignored to some extent. I was writing my own (stateful, one database lookup per request) SSO system and ran into other problems. For example, how do you terminate all TCP connections that the user has open when revoking the token? No proxy I could find had that level of granularity exposed through its control plane API. They are all living in an HTTP/1.1 world where requests were short-lived, and it was acceptable to handle revocation at the next request. I kind of doubt it's ever caused any problems (employees on their last day don't seem to open long-lived gRPC connections to internal infrastructure for whatever reason), but it's one of those things that's going to burn someone someday.
(I seriously considered modifying envoy to keep track of (user, downstream TCP connection) so that they could be forcibly closed via an API when necessary, but decided a global rolling restart of the frontend proxy was acceptable in the extremely rare instance when some sort incident required terminating all of a user's TCP connections. Engineering time vs. security tradeoff. But then there are companies with 100s of developers that will ship you custom software to manage authentication, and they don't have this feature. That seems shady to me. Or maybe not so much shady, just exceedingly lazy, like they haven't really thought much about their product.)
Here's a nice real world example:
* Expensive combination padlock on a cheap shared property. The building owners decided our bin shed should be locked, they bought a nice padlock, installed the lock and mailed out the code, residents don't need the hassle so somebody doesn't close the padlock, thieves who would never want to steal our garbage do want a free padlock... no more padlock.
You give unnecessary JWTs as an example, but here's another way too many people choose the wrong component:
* Public key cryptography is pointless unless your private keys are private. If your system involves centrally minting key pairs and then sending them out to people you don't actually have a public key cryptography system, you've got a secret key cryptography system in which you are (at best) wasting a lot of resources and getting no benefit. Way too many corporate VPNs are like this. Most S/MIME and almost every private SSL CA I've seen is the same.
JWT is a very useful technology, one that increases UX by decreasing overall latency.
Any technology, can be "abused" or used by people that do not understand how it works, and as a result you will have an insecure system.
JWT is a standard for using that technology, and one that makes several design decisions which make it fragile.
This is the difference between, say, the idea of a car (and the benefits thereof) and a Ford Pinto.
Yes, it's called session storage, and it used to be incredibly common. These issues are not "orthogonal", because a primary promise of JWTs were the ability to get rid of that shared session storage and just put that identifying info into the signed token.