
Auth0 JWT Auth Bypass: Case-Sensitive Blacklisting Is Harmful - CiPHPerCoder
https://insomniasec.com/blog/auth0-jwt-validation-bypass
======
CiPHPerCoder
Tired: {"alg":"none"}

Wired: {"alg":"nonE"}

The JOSE standards (including JWT) are a gift that keeps on giving to
attackers.

I designed an alternative format in 2018 called PASETO, which doesn't contain
the JOSE foot-guns. (I'm pushing for an IETF RFC this year.)

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

EDIT: Also, this affected their Authentication API rather than their JWT
library.

If you use their JWT library, well, it certainly _allows_ this kind of
horrendous misuse... but it is not, per se, vulnerable.

~~~
different_sort
Why a new standard than to push for reform to the current standard?

Are they just closely protected by greybeards who won't listen to reason?

Question comes from a true place of ignorance/curiosity, I definitely
understand the need to have unambiguous, easy to implement security tokens
without the foot-guns.

~~~
pillfill
> Why a new standard than to push for reform to the current standard?

Or even just an opinionated library with some basic guardrails to prevent bad
configurations.

~~~
kelnos
That's tempting, but as long as a standard has design flaws, there will be
libraries out there that _don 't_ prevent bad configurations, and people
(through innocent ignorance) will use them and end up in a bad place.

------
thinkshiv
Hi all - Shiv from Auth0. I am the CPO and wanted to share some additional
context here. On July 31st 2019, at 5:11 am, we received an email from
Insomnia reporting a service vulnerability. By 11:00 pm the same day, we had
fixed the issue in production. We analyzed the logs and validated that no one
exploited the vulnerability. More details from our CSO here:
[https://auth0.com/blog/insomnia-security-
disclosure/?utm_sou...](https://auth0.com/blog/insomnia-security-
disclosure/?utm_source=twitter&utm_medium=sc&utm_campaign=insomnia_disclosure).
Thanks to Insomnia for reporting the vulnerability and their partnership in
coordinated disclosure. We appreciate the continued feedback from the security
community-at-large to ensure we are providing the most secure platform for our
global customers.

~~~
treve
Why did your implementation have a case-sensitive check for a fixed list of
algorithms, and why are you blacklisting vs. whitelisting acceptable
algorithms? 'Old, stable' codebase or not... this is production code for a
security product and seems like something that would be picked up during an
audit.

~~~
fulafel
Not the OP but, the sad truth is that code audits aren't that good at
eradicating bugs.

------
Arnout
I've encountered issues like this in various systems using JWT at this point.
The real problem is that developers blacklist the algorithms they don't want.
Instead, the verification code should explicitly whitelist which algorithms
you support.

More specifically, you can't even rely on using the 'alg' parameter before
successful signature verification with any level of authority: after all, it
is protected by the signature it declares the algorithm for itself. So even
with a whitelist, there is the potential of downgrade attacks.

In other words, don't even use a whitelist, use a single specific expected
algorithm.

~~~
applecrazy
> Instead, the verification code should explicitly whitelist which algorithms
> you support.

What libraries are you using? I just looked through the auth code for a
project I'm working on (which uses `jsonwebtoken`) and it has an option to
whitelist algorithms in the `jwt.verify` method.

Edit: removed repeated info

~~~
Arnout
Various, been a while since I wrote code using them myself.

Often JWT tokens come from sources other than our own and they will have
passed through user agent or client land. Don't trust anything in them unless
you verified them.

edit: good on that library! That's what it should do. Clearly auth0's code did
not do that though, it should never have accepted any variant of 'none' in the
first place.

~~~
applecrazy
Just wanted to point out the supreme irony: Auth0 wrote that library.

~~~
Arnout
Hah. They could still improve it by only accepting a single algorithm, rather
than a list.

edit: though there could be some internal use cases where you want a list, but
it's a tradeoff between flexibility and making it easy for people to shoot
themselves in the foot.

~~~
wongarsu
If you don't set it it's set to a reasonable list of algorithms [0], which
doesn't include "none"

[https://github.com/auth0/node-
jsonwebtoken/blob/master/verif...](https://github.com/auth0/node-
jsonwebtoken/blob/master/verify.js#L113)

------
xianb
It's fascinating how Auth0 actually had a blog post about finding and fixing a
handful of JWT vulnerabilities years ago (one of them is more advanced to
exploit than this). Just another example of why you always have to be vigilant
and that properly implementing encryption/security is hard

[https://auth0.com/blog/critical-vulnerabilities-in-json-
web-...](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-
libraries/)

------
twic
At some point, that alg parameter gets resolved to an algorithm - an object or
enum constant or something. This bug implies that the filtering was done on
the string value of the parameter, and not the resolved value. That seems like
a schoolboy error.

~~~
user5994461
It's much worse than that. There are like 5 options for the algorithm value,
none, RS256, HS256, etc...

The vulnerabilities implies that they don't verify the value against the very
limited list of possible values, which is incredibly stupid.

------
rvz
The gist of this Auth0 authentication API bypass is detailed as follows:

> The Authentication API prevented the use of alg: none with a case sensitive
> filter. This means that simply capitalising any letter e.g. alg: nonE,
> allowed tokens to be forged.

I really don't know what to think of why you need a case-sensitive filter for
alg:'none'. The question is that why use and support 'alg:none' in the
standard in the first place? As I previously commented, the option to have
'alg: none' should never be used as it is still the biggest footgun in the
JOSE specification. Even giving the user a choice of ciphers to use is a
recipe for disaster. Thus, JWT is still a cryptographically weak standard and
its use is discouraged by many cryptographers.

PASETO [0] or Branca [1] are cryptographically stronger alternatives to use
over JWT here.

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

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

~~~
applecrazy
> the option to have 'alg: none' should never be used

I doubt anyone uses this deliberately (edit: except maybe for internal server
to server communications?). I agree that having it as an option is a footgun.
I still think this is a non-issue on the client/backend, most libraries
explicitly make you whitelist token signing algorithms and will throw errors
if the token isn't signed with the right algorithm.

> Even giving the user a choice of ciphers to use is a recipe for disaster.

How so? I'm still learning this stuff, so I'm genuinely curious.

~~~
CiPHPerCoder
> > Even giving the user a choice of ciphers to use is a recipe for disaster.

> How so? I'm still learning this stuff, so I'm genuinely curious.

[https://paragonie.com/blog/2019/10/against-agility-in-
crypto...](https://paragonie.com/blog/2019/10/against-agility-in-cryptography-
protocols) :)

~~~
user5994461
I really hope you're planning some agility in PASETO otherwise it's de-facto
s* protocol that will have to be thrown away within a few years upon the first
cryptographic weakness, breaking all applications that dared to adopt it.

Fact is, ciphers and protocols evolve over time. In the real world of client-
servers (often many clients and many servers), it's not possible to magically
upgrade all systems at once to exclusively accept a single same cipher.
There's got to be a way to phase-in ciphers gradually across systems and
phase-off. Agility is simply a real world constraint to be able to operate
software in the real world.

~~~
CiPHPerCoder
> I really hope you're planning some agility in PASETO otherwise it's de-facto
> s* protocol that will have to be thrown away within a few years upon the
> first cryptographic weakness, breaking all applications that dared to adopt
> it.

Instead of cipher agility, PASETO uses versioned protocols.

My DEFCON Crypto & Privacy Village talk (slides and YouTube video at
[https://paseto.io](https://paseto.io) for the curious) covered this
distinction in detail.

------
user5994461
Most JWT libraries require to hardcode the expected algorithm when verifying a
token, so if your applications are verifying the token provided by Auth0 with
a JWT library, they're most likely not vulnerable to this mistake.

------
userbinator
I think what's _more_ harmful is the fact that something is case-insensitive.

Case insensitive may have some benefits for human-facing stuff, but otherwise
the byte-exact comparison you get with case-sensitive semantics is superior.

------
joepie91_
There's actually _three_ lessons to be drawn from this incident:

1\. Don't use JWT, it's too easy to mess up.

2\. If you're trying to fence off some sort of format or API, _whitelist_
things, don't blacklist them.

3\. This narrative that "you should use a third-party authentication provider
because they're security experts and are much less likely to get it wrong"...
well... I think you can see where I'm going with this.

------
gjvnq
What if someone used the correct case but weird Unicode characters? I mean, if
"none" = "nonE", is "none" = "ｎｏｎｅ"

~~~
theamk
The answer to that is we should not be using _any_ case-insensitive strings in
protocols.

They are fine for human-visible names, but field names and internal enum
values should use byte-by-byte comparison. It just makes entire class of
vulnerabilities go away.

~~~
deathanatos
They _are_ case-sensitive.

> _The "alg" value is a case-sensitive ASCII string containing a StringOrURI
> value. This Header Parameter MUST be present and MUST be understood and
> processed by implementations._

~~~
JdeBP
_But the protocol says that they are case-insensitive!_ is not a good response
to the assertion that _machine-to-machine protocols should not be case-
insensitive_ , which is what theamk said.

------
NoInputSignal
I think this points out that the semantics of trusting the header (which is
still a part of the message) at all is flawed and leads to implementations
getting it wrong and leaving gaps for attackers to exploit.

------
tinus_hn
Blacklisting while you should have been whitelisting is harmful. Choose what
you allow instead of trying to list what you don’t.

------
rgj
Without reading the article: any kind of blacklisting is considered harmful in
security.

------
eximius
Always normalize your input?

~~~
JdeBP
Try:

Don't encode your machine-to-machine protocol as human-readable strings,
leading to things like declaring machine-readable identifiers to be case-
insensitive when only the humans need this, not the machines.

~~~
eximius
eh, arbitrary protocols, sure.

But the web has always been human-readable and I don't want to change that.
Therefore, you're gunna get serialized strings.

