Security protocol observations

Does anyone know if Monzo uses JWT for authentication? (Nb. this isn’t in the docs…)

Tl;dr: they do.

And there are lots of reasons they shouldn’t (apparently):

Some thoughts:

You said “Tl;dr; they do” but didn’t really explain where they’re used in Monzo, what they’re used for, what sort of data they encode or why you think that’s problematic other than “JWTs are bad!!”.

So regarding your first link:

Have you got any evidence that Monzo are implementing this in an insecure manner (i.e. with a key of insecure length)?

The article points out that the spec prescribes a sufficiently long secret key (of 32 bytes) and then proves that you can brute force secrets of length … 4 and 6 bytes. Great … don’t use stupidly short keys? I’ll run into the exact same problem if I’m using really short (or even sequential!) tokens which I then look up in the database.

Regarding the second article:

I do prefer to see opaque session identifiers - I’ve seen enough attacks to know that other approaches tend to be implemented insecurely, one of my colleagues broke Laravel’s encrypted cookie system twice. Many of the article’s points have merit. However this doesn’t mean that the alternatives are always implemented insecurely!

These sort of database-backed identifiers have disadvantages, too. Suddenly you need to maintain a session database which becomes a central point of failure. You introduce dependencies on other services, things aren’t as independent anymore.

Presumably the engineers at Monzo and/or @daniel weighed this up when they chose to use JWTs. As always, security isn’t absolute so I’d be interested to hear some more information on Monzo’s particular usecase before concluding anything.

Apologies - they are used in the email you’re sent when you want to login to the app or the API.

I haven’t - I was just posting a few of the links I was sent by someone who took one look at the API and was very concerned that Monzo used JWT. I personally have never used JWT however it concerns me that JWTs are irrevocable.

Agreed. It just seems JWT is pretty universally discouraged so I would wonder why Monzo is using it?

My particular concern lies in the fact it is irrevocable and if they key is ever disclosed (because it is symmetrical) that would be pretty deadly…

From the second article:

the usecases where JWT is particularly effective are typically usecases where they are used as a single-use authorization token.

Don’t Monzo’s magic links fit this bill? Not only are they single use, they’re also ephemeral.

This is very true :slight_smile:

I’m going to follow up and agree with @crablab. JWT’s are used pretty much everywhere in Monzo, not just in the Magic Links:

  1. The Magic Link tokens - they expire after use and there’s no reason why regular key rotation isn’t in place so, as @adam.williams has suggested - really no issues here.
  2. Auth tokens - This is stored in the browser session storage when a user logs in with their Magic token. They contain the ‘jti’ field (to prevent replays, which makes sense) and an ‘exp’ expiration epoch, meaning that they are definitely ephemeral and single-use (again, JWT’s work well here).
  3. Refresh tokens - These are, by both definition and practice, not ephemeral (the MAC secret cannot be rotated). They’re stored user-side and they don’t have an expiration. The saving grace here is that to actually use one, you’d need the registered applications key. However (and I haven’t and can’t check this) if the auth token and refresh token both use the same leaked key then a new refresh and auth pair could easily be forged. Again, if the auth token ‘jti’ is stored and used to reference a session on the backend as well as the perceived guarantee from the authenticated JWT (in this scenario I’m not entirely sure of the benefit of using JWTs).

It’s not that JWT’s are flawed in this sense, it’s just that they require equal or greater infrastructure to guarantee their authenticity if it’s presumed that the key is lost (which, in a bank, it should).


I love the interest in security our community has! I’m going to try and share more security related info over the next few months :slight_smile:

I would agree with the assessment that the JWT spec is not one of the best specs in the world. An ideal spec would be one that you basically couldn’t mess up an implementation of. JWT and OAuth2 do not meet this requirement at all. That being said, that doesn’t mean that it’s not possible to create secure systems on top of these specs - just that it’s also possible to make mistakes that would result in insecure systems.

I could post the JWT signing key here and you would not be able to create a valid access token. You may have noticed a field called eb if you decode the JWT access tokens. This stands for “entropy bucket” and contains random data. When validating an access token we don’t just validate the signature but we validate the value of the entropy bucket against the value that we stored. This means we don’t rely upon the signature, it is just one layer of defence. It also means that we can (and do) revoke access tokens.

If we ever have an access token with a valid signature and invalid entropy bucket then alarms will go off.


That would be great - thank you! :slight_smile:

That is good - I haven’t used JWT abut as you mention, it seems that if the RFC/spec are implemented verbatim then you end up with a system full of holes.


There’s an article attacking JWTs every few months and they’re pretty much always founded on nonsense.

JWTs are perfectly fine as long as you’ve implemented them properly; weak encryption and poor implementations will blight any form of authentication system.


:smiley: Thank you for answering that question! I have been puzzling about what “eb” meant for about a week. The closest we got was “extra bytes”, which I guess is pretty comparable :stuck_out_tongue:


Hey Monzo!

Just having a peruse of your API, and I noticed the example body for the ‘create webhook’ endpoint points to a http:// URL.

There is a growing list of technologies that are only available via HTTPS, and I would strongly urge Monzo to follow this pattern. Especially because of the sensitive nature of Monzo’s business.

There is no argument to be using unencrypted connections anywhere any more, even for local development. If a developer doesn’t understand why unencrypted connections are bad, then they shouldn’t be using the API, for their own protection, and the protection of others.


1 Like

Also just noticed another endpoint ‘Create a feed item’ refers to loading a gif from “”.
Loading this url from a mobile device allows an MITM attacker to inject a malicious image, and also allows the user’s ISP to infer browser activity and user tracking from the headers sent as part of the request.
All because the image was loaded without HTTPS.

2 Likes is served with no security headers. This means an attacker could easily perform a number of attacks on this domain, subsequent to an SSLStrip/HTTPS downgrade attack, without the user’s knowledge.
There are also security issues with other parts of the response, including the session cookie is missing the ‘secure’ flag.
Adding the HSTS header should be top priority here, and this should be applied to all subdomains on

42 BST

1 Like

Time to tag @daniel again

Edit: totally agree, HSTS is a requirement nowadays - there is no good reason not to implement it.


Lack of HSTS on the API isn’t really a big deal given it’s gonna be accessed programmatically and its (HTTPS) URLs will be hardcoded so no risk of SSLStrip either. And even if we had HSTS I have yet to see a REST client library actually support it, exactly due to the reasons above.

Not necessarily :slight_smile:

True. cURL doesn’t which I found a little surprising given that is used for more than just APIs

I can think of plenty of good reasons to implement it :wink:

1 Like

Whoops! Thank you :stuck_out_tongue:

Completely agree - for an API accessed from an app, the developers should be hardcoding the HTTPS’d URL and ensuring proper certificate validation is happening. I’ve never seen a REST client with a HSTS preload list or any sort of mechanism for storing HSTS policies.

However, the API does support CORS so it can be accessed from a webpage with a web browser using e.g. the fetch API. For that reason, HSTS should be deployed.