
Stop using JWT for sessions (2016) - enraged_camel
http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
======
drinchev
Top reason for me always was :

> You cannot invalidate individual JWT tokens And there are more security
> problems. Unlike sessions - which can be invalidated by the server whenever
> it feels like it - individual stateless JWT tokens cannot be invalidated. By
> design, they will be valid until they expire, no matter what happens. This
> means that you cannot, for example, invalidate the session of an attacker
> after detecting a compromise. You also cannot invalidate old sessions when a
> user changes their password.

> You are essentially powerless, and cannot 'kill' a session without building
> complex (and stateful!) infrastructure to explicitly detect and reject them,
> defeating the entire point of using stateless JWT tokens to begin with.

~~~
tehlike
Not true. Encode a token and validate it against a constant, which you change
in case of a compromise.

~~~
drinchev
What's the point of having a token if you need to check it with a database on
every request?

Instead of `SELECT isValid from tokens where token="<TOKEN>"`, why not `SELECT
user from sessions where session="<COOKIE-SESSION>"`?

~~~
kbenson
You don't store the token in the database, you put it in a channel that
deploys with your app and can up updated as needed. That can be a flat file
shipped with production, or if a production push is too costly, set some other
way.

Changing the static token causes all JWT token verifications to fail (since
the token is stored in the JWT),so requires each user re-auth on the next
request. It's a manual fail-safe to invalidate stored sessions.

This, of course can spike load considerably in the period immediately after,
so if that's a problem, you can always use multiple tokens based on some
criteria, such as one matching the first letter of the username, or the user
id mod some value, so split your user into N chunks that each have their own
invalidation token, so you only have to invalidate the sessions of 1/N users.

~~~
enraged_camel
>>Changing the static token causes all JWT token verifications to fail (since
the token is stored in the JWT),so requires each user re-auth on the next
request.

So if a single user's token gets compromised, you force ALL users to log in
again?

~~~
kbenson
No, you force the JWT to be verified against the DB, which might be as simple
as checking if the JWT creation time is older than the password update time in
the DB, and if so, force only those users to log in.

All the token does is force some extra level of scrutiny. It doesn't mean the
JWT has to be invalidated,but it does give you the ability to selectively
invalidate specific ones for the cost of extra DB load _once_ for all affected
users.

~~~
enraged_camel
I see. Thank you for clarifying. :)

Anyway, I'm still not convinced because when it comes to security I feel like
it's a better idea to walk the well-trodden path, rather than invent new and
exciting mechanisms. JWTs are useful for other stuff, but using them for
security requires increasing back-end complexity quite a bit.

~~~
kbenson
Sure, I'm not making a case JWT should be used, just that in the cases where
the benefits it provides can yield real value (e.g. high capacity secured API
with good caching infrastructure behind it), there are mitigations that might
help offset the downsides.

------
franciscop
If an attacker can read the user's localStorage you have much bigger issues to
worry about: XSS, physical access, no TLS, etc. With any of those, the token
is the least of your concerns; an attacker can make API calls, read the data,
etc anyway, no matter if its JWT or sessions.

So, IF (which is a big if) you implement everything correctly then you have to
do two "extra" steps with each tech:

\- Blacklists (or Whitelists). You _do_ need these, because you want users to
be able to logout. Then it's a lot more like sessions... oops.

\- CSRF protection for sessions. These become trickier to manage in SPA and
just become sort of a "token" that you need to refresh on each call... oops.

It seems that the shortcomings of each technology makes it to lean towards the
other. The good news is that both of them are theoretically secure, the bad
news is that to reach that level you have to do a lot of work (on both!) with
a lot of room for error.

That is why I personally prefer JWT with SPA and CSRF with traditional
servers, since the amount of new code that I have to write is a lot lower so
chances for error are quite lower. For instance, see the (very brief)
documentation of the CSRF for my project server.js:
[https://serverjs.io/documentation/router/#csrf-
token](https://serverjs.io/documentation/router/#csrf-token) . If I had more
time I'd write something similar for JWT.

~~~
donatj
"If an attacker can read the user's localStorage"

9/10 the attacker is the user.

~~~
franciscop
Since the topic is authentication, that is an independent concern, right?

Edit: unless you mean something about making the user do something. If it's
running a script, it's the same in both ways. Now, if it is about the user
retrieving and sending the token, the attacker could still ask the user to
manually get the session cookie or the localStorage JWT. The cookies might be
protected not to be accessible from JS, but they are still in the browser.

------
indeyets
It's an interesting reading (together with second part), but should be taken
with a grain of salt.

Stateless sessions are useful under load and not necessarily under reddit-size
load. I like 2-layered setup actually with long-expiration (1 year) "session"
cookie kept on `auth.example.com` domain and short-lived (1 hour) JWT-cookie
kept on `example.com` domain. JWT can optionally include permission for
dangerous operations which is not automatically granted.

You still need redis to take care of forceful expiration, but that would be
very simple call to the small list (1 hour of force-expirations if you reissue
jwt-tokens on hourly basis), not data-fetching calls. We can keep this in a
tiny redis-cluster colocated with frontend machines. JWT would have enough
data to personalize cached templates. This will render really fast.

API-backend, database and larger redis-storages can be kept deeper in logical
server-room and be accessed only on cold requests

~~~
nine_k
I generally agree, but to me "short-lived" would be below a minute. The point
of the short-lived token is to make many clustered requests (as when a page
loads a lot of assets) cheap because you only need to check a token. Any other
human interaction, which takes longer, should go through the server-persisted
session logic.

~~~
indeyets
hi ;)

I guess that depends on exact use-cases. Below-a-minute is fine for tightly
controlled environments, but publicly-used web-apps usually have a sweet spot
with TTLs between 10 and 60 minutes. More requests = shorter TTL, less
requests = longer ttl (as it would be counter-productive to have more auth-
requests than data-requests)

~~~
nine_k
Indeed hi! :)

------
anon1252
The idea of JWT is basically you give your user a "token" that can be used
against many "services" while servers don't have to persist the state of that
token, just be able to read the encrypted token, and trust the information in
the token. The obvious issue is the server cannot easily revoke that token
without blacklisting it, therefore persisting that token somewhere on a
blacklist on the server. If you are going to make a lookup for a token in a
blacklist you might as well look up for a session ID to being with.

~~~
redbeard0x0a
Traversing a list of 100,000 users/sessions in a db to pull up the session is
a different beast compared to traversing an in memory list of 10-100 revoked
JTIs in redis. It is a lot less data to store and optimize (the full session
vs. a small list of revoked JTIs)

~~~
anon1252
> Traversing a list of 100,000 users/sessions in a db to pull up the session
> is a different beast compared to traversing an in memory list of 10-100
> revoked JTIs in redis. It is a lot less data to store and optimize (the full
> session vs. a small list of revoked JTIs)

I don't see why one can't use redis to persist sessions at first place and why
your list of revoked token would be limited to 100.

~~~
finder83
It does depend on how many users you have and your TTL for the JWT, but if you
have a 10 minute time to live, you only need to store the revocations for 10
minutes in redis since otherwise the tokens themselves expire. (Just an
example, sub 10 minutes with 24 hours or whatever)

------
pentestercrab
This JSON Web Token Security Cheat Sheet[1] or this python script[2] are both
quite useful when reviewing anything using of JWTs.

[1]
[https://assets.pentesterlab.com/jwt_security_cheatsheet/jwt_...](https://assets.pentesterlab.com/jwt_security_cheatsheet/jwt_security_cheatsheet.pdf)

[2] [https://github.com/ticarpi/jwt_tool](https://github.com/ticarpi/jwt_tool)

~~~
todd3834
Those are great! I find the OWASP cheat sheet[1] useful as well but I’m going
to keep the pentesterlab cheetsheet bookmarked for easier to digest to similar
data. Thanks for sharing!

[1]
[https://www.owasp.org/index.php/JSON_Web_Token_(JWT)_Cheat_S...](https://www.owasp.org/index.php/JSON_Web_Token_\(JWT\)_Cheat_Sheet_for_Java)

------
AndrewSChapman
I'm not 100% sold. I think JWT's are fine in some situations as long as you
know the limitations.

Regarding session invalidation, I would handle this in two ways.

1\. Support a "Log everyone out" function by storing a simple version number
in the JWT. If the version constant in your app is different to the number in
the JWT, it is invalid.

2\. Support a "I need to logout user X function" by storing a blacklist of
tokens in your RDBMS. If your RDBMS goes down, you have much bigger problems.
A blacklist will be very short, and often completely empty, so lookups will be
significantly faster than checking a session table. You could also use Redis
or an application cache for storing the blacklist, with an RDBMS fallback.

Regarding storage, there's nothing stopping you putting your JWT into a cookie
with the HTTPonly flag set. So long as you're not storing too much in token,
they will be less than 4k and so will fit.

~~~
enraged_camel
>>2\. Support a "I need to logout user X function" by storing a blacklist of
tokens in your RDBMS.

Again though, the whole point of JWT is to completely avoid server-side state
management. The moment you store state somewhere (whether a list of valid
tokens or a shorter list of invalid tokens) you have re-invented the concept
of sessions, so why not use that instead?

~~~
AndrewSChapman
How about instead of "the whole point of JWT is to completely avoid server-
side state management" we think of it as "the whole point of JWT is to reduce
the overheads of server-side state management".

In my answer above, I was pretty clear about how the load on the database can
be significantly reduced with a blacklist and optionally Redis out front, thus
allowing JWT to significantly reduce the burden on the database.

~~~
therealdrag0
Why would the lookups be faster in a small/empty table? If the lookup is by
indexed ID it should be trivially fast either way.

Furthermore, you can store the session information in Redis by ID, too, to
reduce DB burden.

------
ThePhysicist
I used to be very opposed to JWTs but I can see some interesting use cases for
them now, at least when using the variant based on public key cryptography.
For example, if you need/want the ability to verify token validity locally
(i.e. without making a call to you auth backend) they are very handy: Generate
a token, sign it using the auth backends' private key and clients can verify
it using the public key.

That said, I wouldn't use them as a default authentication mechanism as
invalidation requires a connection between clients and auth backend again, as
many people here pointed already out. It might still be easier to establish
such a channel though than continuously verifying traditional tokens via the
backend, which you can solve differently though as well: For example, for our
APIs at KIProtect we cache validity information for (hashed) access tokens for
60 seconds on the API server, and we refresh the tokens in the background 30
seconds before they expire (if triggered by a request). Like that we can
ensure that invalid tokens cannot be used after a short grace period (60
seconds is good enough for us) while not slowing down clients that perform
many API requests as the token needs to be fetched only for the first request
and will then never leave the cache (as it gets updated) if the client
performs at least one request roughly every 60 seconds (or more often).

------
ccleve
Sorry, I'm going to be a contrarian here. JWTs can be good.

First, cookies-vs-local storage is a non-issue here. If a JWT is small, it can
be put in either location.

The only real issue is, should session state be on the client or on the
server?

Suppose you have an app with large numbers of users, doing hundreds+ of
queries per second across many boxes. If you put session state on the server,
then you must either 1) have sticky sessions, which generate problems when a
server goes down; 2) have a central session server, which generates
scalability and reliability problems; 3) have a Redis-like distributed system,
which either a) must do one or more server-to-server round trips on every
client call to validate the session, or b) cache session information on a
node.

When latency matters, an extra server-to-server round trip is a non-starter.
And if you do server-side caching, then cache invalidation and JWT
invalidation present similar problems. ("There are only two hard things in
Computer Science: cache invalidation and naming things.").

Server-side sessions don't really buy you much. If you're using a system that
has them built in, lovely, but if you're not, it's a lot of extra work.

JWT is a good solution to the problem. You can store _small_ amounts of
session state on the client and simplify the whole system. You can avoid
having to do lookups to determine state variables. (What database was the user
connected to? Is he in mode A or mode B? What timezone? What language?) Put a
short expiration on the token (a minute? five minutes?) and force the user to
re-validate against some data store after that.

The benefit is that you greatly reduce internal server traffic at the cost of
not being able to invalidate a session within your short timeout interval.
Depending on your app, letting someone remain an admin for an extra minute is
not a big deal.

And if it is a big deal, you can still handle it by sending a message to each
node telling it to invalidate that particular user. A pain, yes, but the
tradeoffs may be worth it.

~~~
CiPHPerCoder
> If you're using a system that has them built in, lovely, but if you're not,
> it's a lot of extra work.

They're built in with PHP and most Node.js frameworks I've ever worked with.

I'm not sure about Python, RoR, etc. web frameworks but the engineering
overhead for adopting server-side sessions _should_ approximate 0, since
they're usually the default.

~~~
alliecat
Ruby dev here, Rails is pretty good at sessions. I hear the Python crowd have
some great stuff too.

------
mgraczyk
Like many other articles on JWTs, this one mischaracterizes the trade-offs
that exist.

You can invalidate individual JWTs, you simply store the blacklisted token IDs
in a table in your database. Implemented naively, this results in a request
flow in which the database is still accessed on every request, defeating one
primary purpose for using JWTs. However, there are better ways.

Since the blacklist is just a collection of random token IDs, it isn't
sensitive. You can store it anywhere, including in memory in your web servers
or in a distributed cache. You can wait during blacklist/token invalidation
events until the newly blacklisted token has been propagated.

Using the above, JWTs enable a design space trade-offs that doesn't exist with
session tokens. You trade slower blacklist requests (logout, one time
confirmations, etc) and minor auth complexity for one fewer db access per
request, more flexibility, and more uniformity if you use oath or similar.

All of this is around 90 lines of python in my Django site.

~~~
KirinDave
> Since the blacklist is just a collection of random token IDs, it isn't
> sensitive. You can store it anywhere, including in memory in your web
> servers or in a distributed cache. You can wait during blacklist/token
> invalidation events until the newly blacklisted token has been propagated.

This is very dangerous advice. If you're advocating for the addition of a fast
blacklist storage, you really haven't changed your previous pattern complaint,
but the dangeous bit of your advice is implying that it's simple to store
"anywhere" such as "in memory in your web servers."

 _That is a bad idea_. If your authentication model is depending on an
explicit stop-list to invalidate tokens, then you need to take the integrity
of that data set very seriously or be at serious risk of credential replay
attacks. A loss of integrity in this data store in unacceptable. Even some
eventual consistency can be dangerous (but is generally accepted as a risk if
the time envelope is acceptable).

It's much better (from a security standpoint) to have very short lived tokens
and constantly revalidate. The revalidation cycles CAN be full database cycles
since they occur at a reduced rate, and you can then have more sophisticated
policies there without too much trouble.

> All of this is around 90 lines of python in my Django site.

I don't doubt it, but trying to scale your advice to a product or to an
environment with actual concurrency opens up a lot of complications you're not
considering.

JWTs are, by and large, just an inferior spec with too many brittle edges. You
need to be really careful to properly implement JWTs in your environment.
You're much better off either sticking to rich session tokens or using
something like Macaroons.

P.S., not only is the phrase "black" list steeped in a history you may not
realize you're invoking, but it's much less precise than the term "stop list".
Please do consider this.

~~~
mgraczyk
> then you need to take the integrity of that data set very seriously or be at
> serious risk of credential replay attacks.

This is no more true in my technique than in session tokens. You have to
invalidate session tokens carefully, most simply using serializable database
transactions. You can do the same with the blacklist.

People also put session tokens in memory, and your concern about caching and
consistency applies equally in that context. The blacklist is in some ways
easier to manage because it is not sensitive like a session token. It's more
difficult in other ways, but IMO harder to get wrong.

~~~
KirinDave
> This is no more true in my technique than in session tokens.

Which is to say that it's equally true, but you're advocating for a more
ephemeral storage.

> People also put session tokens in memory, and your concern about caching and
> consistency applies equally in that context. The blacklist is in some ways
> easier to manage because it is not sensitive like a session token. It's more
> difficult in other ways, but IMO harder to get wrong.

If you're leaking session tokens your entire authN/authZ system collapses I
guess that's not wrong, but it's not clear that your invalidation session is
as insensitive as you suggest. For example, if your token generator's RNG has
flaws, you're essentially enabling that discovery via a public stoplist store.

Ultimately, it's weird to imagine using something like JWT and having a
request-to-authZ scheme. You have all the equipment to do short-lived
renewable tokens, and then you get invalidation (to the resolution of your
timing) for free and better scaling properties and a whole host of other
security benefits.

I personally strongly discourage people to use JWTs at all, but if you are at
least take advantage of them!

------
ivanb
Many in the comments are curious about blacklisting JWTs. No, you don't need
to put the whole token on a blacklist. Give your tokens reasonably unique JTI
claim[1] and put the affected JTIs on the blacklist.

The benefit of JWTs is that token blacklists are usually much shorter than
hypothetical token whitelists.

[1]
[https://tools.ietf.org/html/rfc7519#section-4.1.7](https://tools.ietf.org/html/rfc7519#section-4.1.7)

~~~
the_af
What if the blacklist becomes unavailable? The author addresses this in part
2.

~~~
xyzzy123
Then your application is unavailable, just like it would be if your session
table went away? I don’t get this complaint.

The advantage is that you can use a loosely consistent datastore with expiring
entries and few records at any given time. There’s lots to love about the
approach.

~~~
the_af
> _Then your application is unavailable, just like it would be if your session
> table went away?_

The author replies: "congratulations, you've just reinvented sessions, with
all their problems (like centralized state) and gained nothing in the
process", and also with an implementation that is "less battle-tested".

My own opinion: loose consistency can work in some circumstances, but seems a
security risk in others.

~~~
Spivak
But we've drastically reduced the amount of central state, the complexity of
managing it, the time it takes to access it, the danger of losing it, the
performance impact of updates to it, while making the happy path to your site
faster.

Revolutionary, maybe not, a solid improvement on the traditional design of
sessions, I think so.

------
arendtio
> Unless you work on a Reddit-scale application, there's no reason to be using
> JWT tokens as a session mechanism.

And what should I use when I do work on a Reddit-scale application?

~~~
captn3m0
[https://paseto.io/](https://paseto.io/)

>>Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the
many design deficits that plague the JOSE standards.

------
fatpigeon
Previous discussion:
[https://news.ycombinator.com/item?id=11895440](https://news.ycombinator.com/item?id=11895440)

------
jesseb
From my time browsing HN, the consensus seems to be "don't use JWTs". What is
the preferred method of client authentication for simple APIs these days?

~~~
scient
OAuth access tokens? The difference being that JWTs contain information
directly, as well as an access token to hit any APIs, while an access token
contains no information on its own - thus solving the revocation issue.

Now I don't think anyone really uses JWTs for API authentication, or they
should not at least. APIs should still be gated by access tokens.

------
jcadam
I use JWT for authentication on my current project. And I eventually ended up
having to check the user's 'role' in the database for authorization before I
let them do anything anyway (and consequently, revoking someone's user role or
suspending their account has immediate effect).

I saw many examples on the net of people putting role/authorization and
various other types of session information within the JWT and it just looked
insane to me.

Ultimately, I think what I ended up with is only a minor win over just
generating random tokens and maintaining a "sessions" table. Probably not
worth the effort and likely won't use JWT again.

~~~
wheresvic1
> I saw many examples on the net of people putting role/authorization and
> various other types of session information within the JWT and it just looked
> insane to me.

Why is this crazy? If you encrypt your JWT with a private key that is only
known to the server, then any modification to the JWT will lead to an invalid
token.

The only thing here is that the list of roles granted is visible to the user
if they were to decode the JWT, which is something you might not be entirely
comfortable with but I don't think it's totally crazy.

~~~
jcadam
I suppose it depends on the application. I wanted to be able to revoke a
user's permissions and not have to wait until token expiration for them to be
locked out.

------
H00tyMcOwlFace
Burp Plugin for JWT decoding / attacks
[https://github.com/mvetsch/JWT4B](https://github.com/mvetsch/JWT4B)

------
Double_a_92
Can anybody recommend a tutorial or book on how to do Authentication properly?
Say for the usecase SPA + REST API.

If I google quickly I mostly just find tutorials that turn out to be ads for
some 3rd party service. Or other beginner tutorials that don't seem very
trustworthy.

I feel like I'm missing something, since this should be a very common use case
for most (web) apps.

(Bonus if it also covers User Authorization, i.e. different roles that are
allowed to do things or not.)

Thanks.

------
coltonv
We use JWT for validating simple handoffs from server to server, allowing
someone to briefly access a resources such as downloading a file from a server
designed to do that. We set the expiration to only an hour, and JWT is
excellent for this use case as it means we don't need to implement our entire
login and database workflow on a machine that just serves files.

------
tebruno99
"You cannot invalidate individual JWT tokens"

This is not exactly true. There are lots of things you can do to expire
tokens. For instance, every JWT has a creation date stamp so you could say on
the server side all tokens created before Time.Now() no longer accept as
valid. Allowing users to expire ALL tokens, etc.

~~~
zimpenfish
> you could say on the server side all tokens created before Time.Now() no
> longer accept as valid

But that's not anywhere near "invalidate individual JWT tokens" \- you're
invalidating -every- token before `Time.Now()`. Unless you store a different
"valid after" time per user and then you're back to storing sessions in the
DB, aren't you?

------
Exuma
Can someone explain how this is similar/different from the default ways Ruby
on Rails stores sessions?

I know they're encrypted and signed, and all the session data is stored in the
cookie. This seems to work great, what is the downside of just doing this
method (for say, bigger sites)?

~~~
bradstewart
The only downside I've ever encountered to Rails default sessions involves
authenticating against other backend endpoints from JS applications served by
Rails. Which really has very little to do with Rails itself...

~~~
Exuma
So do you know the difference between that way and the JWT way? All these
different methods are kind of confusing, and it seems Rails does it a great
way, and everyone hates JWT... but arent they basically the same thing?

------
mmgutz
The arguments here says you can't invalidate a JWT. Exactly!! JWT IS A TOKEN.
You can use it for refresh (invalidation & renew) and access (authorization)
tokens. There's so much disinformation about JWT. The end result is some not
invented here "JWT".

~~~
haggy
Yes but you're missing an entire chunk of context. The problem mentioned in
this article is saying it's bad that you can't invalidate a JWT WHEN using
them for authentication and/or authorization. This makes it impossible to do
things like disable/kill a session that has been deemed hostile or dangerous
(like a stolen account).

Now to build on this, good defense-in-depth can help here. For example if you
use JWT only for authn but kept authz 100% server-side. You could then revoke
the users access to everything so that the blast radius is confined. The bad
actor could still authenticate into the system but would not have access to
much or any resources.

------
Tade0
A huge part of the issue with JWT is that it's often used wrongly.

I'm currently in a project where JWT is used. We have a list of searchable
items and each one of them holds a gallery of photos - up to 5MB per photo.

The issue at hand is that these photos are served from a certain endpoint
which we eventually want to secure, but the back-end's idea here is for the
front-end to download the photos via XHR putting the token in the
_Authorization_ header - they refuse to send the JWT via cookie saying that
it's "incompatible with JWT".

------
nbevans
JWT is an authentication token - it isn't session state. Session state changes
during the lifetime of a session. Whereas the authentication token does not -
other than for renewals. Overloading the authentication token to also store
unrelated domain concepts such as shopping basket items is a gross abuse of
its purpose. Given that generating a JWT token is computationally expensive I
really doubt that many people are doing this like the premise of the article
suggests.

~~~
the_af
I think by payload they mean the JWT can "store" actual permissions. Not state
such as a shopping cart, but a payload composed of permissions granted to this
client to do this or that in this app.

~~~
nbevans
Storing security claims i.e. permissions in the JWT token is a valid use. This
isn't session state. Those permissions don't change, they are in fact
cryptographically immutable, until the next renewal of the token. Whereas by
definition "state" does change.

~~~
the_af
I know it's a valid use (I use JWTs!). But it _is_ state, which is why you
have a payload and not merely an id. State doesn't have to change; that's
_mutable_ state. (Permissions do change, by the way, only less frequently than
a shopping cart!).

------
csours
We have a requirement that users be logged out after 30 minutes of inactivity,
so JWTs are perfect for our use case.

~~~
scient
Except cookies are quite literally perfect for that - set the expiration to 30
minutes from issuance and it will automatically be expired with inactivity. Or
bumped on activity.

~~~
deathanatos
You shouldn't be trusting the client to expire your sessions for you. (That
is, relying on a cookie's expiration to actually expire the session.)

(Now, if you mean looking up a session token in a database and checking _that_
for expiry, then yes, that works, but the only real difference between JWT and
tokens then is the latter requiring a database query.)

~~~
yen223
The more important difference between JWT and tokens is that the former
requires you to trust that your encryption mechanism is foolproof, whereas the
latter doesn't.

~~~
deathanatos
Session tokens (an unguessable identifier that identifies server-side stored
session data), while they don't need to be encrypted or signed, the identifier
should usually be cryptographically random, however, so you can still get into
trouble there.

JWTs don't have to be encrypted. (And IME, typically aren't.) They need to be
signed, but the algorithms that do this are common, well-understood
algorithms. Some of them are likely being used by TLS to protect your session
anyways. If they're _not_ foolproof, you have bigger issues than JWT.

If you are referring to the implementation, then sure, but the same concerns
exist about your implementation of TLS, and the same advice applies: use a
well-known, hopefully well tested library.

------
sebringj
Short lived JWT is ok for stitching desperate micro services together as it
saves round trips to verify things but I prefer mystery tokens when using more
intimate services that have access to similar underlying stores to verify
them. Basically use them appropriately and they are fine.

------
novaleaf
How I use JWT:

an encrypted storage of the user's authentication credentials, stuffed into a
persistent domain cookie.

Thus, JWT is just a standardized way of encrypting a json payload and
storing/retrieving it in a cookie.

works for me (my arch is stateless), and seems orthogonal to the arguments
described in the article.

------
staticassertion
Can someone explain the Cookie vs LocalStorage thing? You can access cookies
from Javascript, so how is localstorage worse? Assuming an attacker can
execute arbitrary js in the browser (the model provided by the article).

edit: Thanks for the answers - httponly, makes sense.

~~~
labourcurious
The author glosses over this but what they mean is that you can set the
HttpOnly flag on cookies to prevent them from being accessed via JavaScript.

~~~
akarambir
But is it still sent automatically on ajax requests by browsers?

~~~
SahAssar
Yes.

------
captn3m0
Relevant: [https://paseto.io](https://paseto.io) Platform-Agnostic Security
Tokens

>Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the
many design deficits that plague the JOSE standards.

------
joepie91_
An update that has not been added to the article yet due to a lack of time:
even if you _do_ have a valid usecase for stateless tokens, you probably want
PASETO[1] instead of JWT, due to the inherent cryptographic design flaws
within JWT[2].

I'm probably not going to engage in the discussions on here this time, as it
was an incredibly mentally draining experience last time; the vast majority of
arguments were either already addressed in the article itself (which the
commenters clearly had not read and understood in full), or argued against a
misrepresented claim. It got so bad that I had to dedicate a whole second
article to it[3].

If you have a genuine question or concern about the article rather than an
Angry Internet Comment, feel free to e-mail me; my contact details are at the
bottom of the article. No guarantees on response time as I'm juggling a lot of
other plates at the moment, but I'll eventually get around to responding :)

EDIT: Another addition... this presentation[4] gives some insight into the
history of JWT-for-sessions and why it became so popular. A spoiler: it wasn't
for rationally-technical reasons.

[1] [https://paseto.io/](https://paseto.io/)

[2] [https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-
ba...](https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-
that-everyone-should-avoid)

[3] [http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-
fo...](http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-
part-2-why-your-solution-doesnt-work/)

[4]
[https://youtu.be/GrLtOjCTB1s?t=1h2m44s](https://youtu.be/GrLtOjCTB1s?t=1h2m44s)

------
arithma
Are there any engineering books that have a collection of best practices for
internet engineering problems similar to this one? I find that most of this
information is learned colloquially, or through what other frameworks do.

------
peter_retief
I used JWT for a SPA site using Vuejs with Django backend. The project became
a nightmare and I had to abandon it because of the increasingly complex
security issues. The client was not happy

------
justaaron
what about setting a secure cookie with a jwt, and expiring it when you log-
out? what is the utility in server-sessions? (aside from the ability to revoke
it at any time from the server.)

~~~
anon1252
> and expiring it when you log-out?

and how would you do that if the user doesn't actively log out of a service?

~~~
justaaron
set the cookie in the response to their next request to you?

specifics depend upon the language and framework used on the server, etc...

------
haloux
None of what the author is writing about concerning the inability to revoke
stateless tokens is particularly novel. We have the same problem with SSL/TLS
PKI. Once a cert is revoked, there is no reliable way to ensure that its
revocation is enforced among all clients. The world has proved to be content
with accepting this risk.

Protip: be sure to read what I said before you use the b-word in your
responses.

Additionally, the sarcastic flowchart that is included in part 2 of his post
makes some pretty open-ended assumptions. "Just take down the blacklist
server" is my favorite.

edit: clarification.

------
Charlie_26
This seems to be targeted at web apps. I take it I don't need to remove JWT
token support from all of my mobile applications?

~~~
KirinDave
The problems with JWTs go well beyond "how do we do session invalidation."
However, since mobile apps tend to be SSL encrypted on devices that don't
reveal contents to users, you've often got a lot more leeway than the
transparent condition web apps suffer.

I know my app didn't have any real security challenges until we had a website
with actionable data.

------
ninjakeyboard
I wrote a scala JWT library on a weekend when I was more of an intermediate
developer (6 years ago?) and it started to be used. But I never ran it in
production and flaws were found in it. As a consultant 5 years later I found a
customer using it and told them to use someone else's :).

I'm not a security expert - one of the strengths is one of the weaknesses -
that's it's a simple spec to implement so your average Joe can implement it,
but imperfectly.

------
jopsen
Wow, I wish people would stop complaining about JWTs..

JWTs are a silver bullet, but it's nicer than rolling your own signing scheme.

Don't run with scissors, don't do security if you don't understand your
primitives..

~~~
CiPHPerCoder
The alternative to JWTs isn't rolling your own signing scheme. Use PASETO
instead. [https://paseto.io](https://paseto.io)

But the issue being discussed in this article is about _how_ JWTs are being
used, not JWTs themselves.

> don't do security if you don't understand your primitives..

The security industry needs to do more to provide tools that _do security_ for
people who don't understand their primitives.

------
suff
Obviously written by a non-expert. Half of the claims here are false and he
only references other blogs. OWASP is nowhere to be found.

~~~
0xffff2
This comment is completely unhelpful without detailing which half.

~~~
franciscop
Obviously written by a non-expert /s

------
manigandham
Incorrect, these are not comparable things.

JWTs are a standardized way to securely encode data into a self-contained
token. Cookies are special headers added to HTTP requests to store state.

You can _absolutely_ use JWTs as the payload of your cookie. _How you pass_
the session data back and forth is separate from _how you encode_ the session
data. Most web frameworks have their own token formats, but you can easily
change them to be JWTs.

Using the Authorization header just means you're using a different header, and
since it's not automatically sent then you don't have the CSRF issues with
browsers, but now you have to manage it more manually in your client-side
code.

Regardless of how you encode your tokens, if you want to do instant expiration
or other live data lookup then of course you'll need to manage that on your
server, whether it's a database, in-memory cache, or something else. The split
between where state is managed is irrelevant to the physical transfer and
encoding.

~~~
CiPHPerCoder
> This is just FUD.

No, it's an engineering argument that makes several testable claims about
JWTs, sane session protocols, and how the two are mutually exclusive.

> JWTs are a standardized way to securely encode data into a self-contained
> token. Cookies are special headers added to HTTP requests to store state.

The difference is this:

    
    
      - Sane sessions
        - Random unique identifiers stored in a cookie
        - Upon receiving the cookie, the web app looks in the
          filesystem/database/etc. for the data associated with
          that session
        - Can be invalidated by deleting the server-side storage
      - JWT-based sessions without server-side persistence
        - All session state is encoded into the cookie
        - All the trust is outsourced to the client to invalidate
          sessions.
    

The other arguments follow from this difference.

A strongly worded argument against a bad engineering argument is not FUD.

~~~
manigandham
As stated, how you encode state is different from how you transfer state. It's
not an engineering argument if you don't understand the fundamentals.

JWTs are a encoding format. You can just put a single random unique identifier
in there if you want. And you can use cookies to transfer JWTs automatically
instead of using the Authorization header. Either can be checked on every
request by the server for further validation.

The limitations you wrote are completely arbitrary.

~~~
CiPHPerCoder
It sounds to me like you don't understand what Sven is arguing against in the
article.

> JWTs are a encoding format. You can just put a single random unique
> identifier in there if you want. And you can use "cookies" to transfer JWTs
> automatically instead of using the Authorization header. Either can be
> checked on every request by the server.

This is totally irrelevant!

Does your cookie (JWT, PASETO, or whatever) contain more than just a unique
identifier?

The argument is: Are you storing things like user_id=13455&is_admin=false in
the cookie instead of having that entirely server-side? Don't do that, it's a
bad design, store that server-side instead.

That's literally the argument.

~~~
manigandham
How you ENCODE data is different from how you TRANSFER data and neither are
related to storing state completely on the client vs the server.

You can pass JWTs as cookie data, and you can store nothing but a single
session ID in the JWT itself. They are completely orthogonal and have nothing
to do with the actual argument. It's misleading when the title and the entire
article talks about JWTs vs session/cookies.

~~~
CiPHPerCoder
> That has nothing to do with JWTs.

Progress! You're so close to understanding the article.

It wasn't ever about _JWTs in particular_. It was a response to a widespread
engineering antipattern of "storing all session state in a cookie then maybe
encrypting or HMACing it so you don't have to store anything server-side". JWT
was just how developers tended to implement this antipattern.

Like, literally, that was the _entire_ point of the _entire_ article that Sven
wrote.

The examples of apps that do this have become less common since the article
was written, so if you're confused by that, you're just missing context, and
that's OK.

As for "anti-JWT" arguments, I've made them separately from the argument of
Sven's claims. JWT is an error-prone cryptographic design. I wrote a
replacement standard called PASETO that doesn't contain these foot-bullets. _I
still don 't recommend people use PASETOs for sessions!_

~~~
manigandham
Yes I understand that it's about client vs server managed state and the
balance between. You can see this noted in my very first comment.

But this article is titled and talks about JWTs at great length with pros and
cons, which is completely misleading. The fact that many of the comments here
are only talking about JWTs vs cookies and skip over what and where the state
is managed only further reflects that confusion.

Perhaps a better article should be written and posted.

