
Hardcoded secrets, unverified tokens, and other common JWT mistakes - todsacerdoti
https://r2c.dev/blog/2020/hardcoded-secrets-unverified-tokens-and-other-common-jwt-mistakes/
======
rvz
Well the fact that you need to 'choose' a cryptographic protocol out of the
suite of ciphers reminds me of why the creator of WireGuard opted for no
cryptographic agility and instead chose versioned protocols, unlike OpenVPN.

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.'

[0]
[https://www.wireguard.com/papers/wireguard.pdf](https://www.wireguard.com/papers/wireguard.pdf)

~~~
jasonjayr
If someone wanted to utilize JWT in a new system, and therefore could freely
choose any of the JWT options, what would the "most secure" be?

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?

~~~
user5994461
Here's your recommendations:
[https://security.stackexchange.com/a/233863/111020](https://security.stackexchange.com/a/233863/111020)

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.

~~~
drivebycomment
Good recommendation. I should add that RSA is faster on verification but
slower on signing than ECDSA. For most application, the amount of traffic is
small enough that the difference doesn't matter but for large enough traffic,
you should consider the difference. The performance of course depends on a lot
of factors but

[https://connect2id.com/blog/nimbus-jose-
jwt-6](https://connect2id.com/blog/nimbus-jose-jwt-6)

would give some reasonable idea on the practical difference on modern
hardware.

------
afandian
These failure modes have been used to criticise JWT over the years. But all of
these errors are of the same level as "don't concatenate input strings into
SQL strings" or "don't store passwords in plain text". That these errors are
made says much, but not about the technology, IMHO.

~~~
danjac
An authentication protocol that has this many footguns and insecure defaults
is probably just not a very good authentication protocol.

~~~
user5994461
Irrelevant. If the application doesn't call the function to verify the user
token, the application is insecure, it doesn't matter what protocol or token
format is used.

~~~
chriswarbo
My response to that would be "Parse, don't validate" ( [https://lexi-
lambda.github.io/blog/2019/11/05/parse-don-t-va...](https://lexi-
lambda.github.io/blog/2019/11/05/parse-don-t-validate) ).

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.

------
kkm
Thanks, for the article. Always good to refresh the common pitfalls.

There is a RFC which also details the best practices for JWT: \- JSON Web
Token Best Current Practices:
[https://tools.ietf.org/html/rfc8725](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](https://tools.ietf.org/html/rfc6819)

\- OAuth 2.0 for Browser-Based Apps - [https://tools.ietf.org/html/draft-ietf-
oauth-browser-based-a...](https://tools.ietf.org/html/draft-ietf-oauth-
browser-based-apps-06)

------
ehutch79
Reminder: If your jwt only contains a session_id from your framework, which
you then look up in a database every request, you absolutely should not be
using jwts.

~~~
tester756
I'm using it for cross server auth

------
bstar77
Regarding leaving the secret in your codebase during development... always
make sure the secret is not in your git history. Change it when you move it
out of the repo. I know this seems obvious, but I always do a git history on
files that might have tokens or secrets in history and you'd be surprised how
often I discover them.

~~~
mkagenius
> you'd be surprised how often I discover them

Oh yes, now defunct but we had a search engine just for that :|
[http://archive.is/0GNLl](http://archive.is/0GNLl)

------
ddevault
I evaluated JWT recently and found it so overcomplex and full of footguns that
it was unacceptable for a security-critical component. It boggles the mind
that this was designed as recently as 2015.

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.

~~~
kamyarg
Can you elaborate on this?

> ... full of footguns that it was unacceptable for a security-critical
> component.

What it not secure about it?

~~~
ddevault
See TFA for a subset of the problems. Security should be designed so that it's
easy to do it right and hard to do it wrong, but JWT offers many opportunities
to do it wrong. Maybe you can figure it out with enough reading of TFA and
other articles online, but there's no reason to take a risk on such a shoddy
security standard in the first place.

------
asdjlkadsjklads
Interesting article. On this note, recently as part of small group of people
we needed to implement API -> API authentication. Unfortunately, we don't have
anyone who has solely implemented this before, so we took to searching to try
and pick something resembling industry standards. Of course Oauth 2 got
brought up, however the spec and articles on it seem bizarrely out of sync
with out needs. Notably on two points:

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.

~~~
411111111111111
most of my knowledge about oauth is about using it at my dayjob for years, so
don't take my answer with any authority. its more a users perspective of the
technology then how it was actually meant to be used, which i do not know.

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.

~~~
asdjlkadsjklads
> 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

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

~~~
411111111111111
You should probably just try out a foss oauth server to get a feel for it.

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

------
kevsim
Simply not hard coding your secrets is a good first step. Even better is to
think about how you’re going to rotate secrets. So much easier if you build it
in from the start than if you need to tack it on later. Once you have secret
rotation supported, you can set up mechanisms to do it regularly and you can
rest a bit easier at night.

~~~
nobleach
This is why looking into JWKS might be a decent first step.

------
jkoudys
We'd recently moved our jwt code into rust (using the jsonwebtoken crate), and
it's wonderful to have this article to read in that context. The pitfalls
listed in the article were mostly handled by design, so we had to correct
mistakes in the PHP implementation that were impossible to make now.

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.

------
Lurkars
I only see a list if four "Don'ts". Would have been nice to have some "Do
instead's". Of course I can try to think about "good" ways to access my secret
other way than hard coded, but then I may not be the audience. If I already
made the mistake of storing keys hard coded, then how should I know that my
new way is better than this?! For articles like "X mistakes in topic Y" I
expect some solutions to solve the problems. The other way is only pointing at
fools for me.

------
jakelazaroff
This otherwise good list omits the most pervasive mistake, which is not to use
stateless JWTs for authentication.

~~~
hummo56
Could you elaborate on the why here?

~~~
chriswarbo
From my understanding, the use case for JWTs is where:

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...](https://github.com/paragonie/paseto/tree/master/docs#was-stateless-
session-tokens-one-of-pasetos-design-goals) )

~~~
Snawoot
JWT (specifically JWS) is essentially a simplified modern alternative to X.509
certificates and I think it should be perceived in that way.

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.

------
hoppla
Weak secrets is also a common issue. HS256 and similar algorithms allow for
offline bruteforce attack.

~~~
rmetzler
I would guess that secrets are also often reused over different environments
like test and prod.

------
kamyarg
Anything when not used as it was designed to be used is dangerous.

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.

~~~
XMPPwocky
The technology here is digital signatures.

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.

------
jeremycw
I remember JWT coming on my radar around 10 years ago during the height of
RESTful bikeshedding. It was a "must have" because sessions, being stateful,
are not pure REST. Now that we've come off the height of the pure REST
fanaticism and have learned about a bunch of issues that JWT causes, is it
really better than just having a session id and a session stored on the
server? Does it actually provide any technical benefit? Honest question.

~~~
brown9-2
These are orthogonal concepts. A session ID is just a token that the server
uses to lookup some state about the request that presented the token. A JWT is
a token that can be used to present a claim of who the requestor is (and the
server can verify it). A session ID token doesn’t help my request prove I am
who I say I am when I call your API for the first time, unless you’ve
implemented some sort of state store that all of your API services and server
share.

~~~
hn_throwaway_99
> unless you’ve implemented some sort of state store that all of your API
> services and server share.

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.

