3 min read

JWT and HMAC in the browser, safe?

Is using JWT and HMAC in the browser, safe?

How could they be?

Don't they require a pre-shared secret? How can it be "secret" in the browser!?

Secrets can actually be "secret" in browsers and apps, but almost always isn't securely shared with the client..

If you participate in bug bounty programs like HackerOne, Bugcrowd, Synack Red Team, or YesWeHack then you'll know that finding a HMAC secret in the browser of the logged in user will not pay a bounty, unless one of the following can be proven;
1) This is a pre-shared secret that allows actions other than the allowed user actions - privesc
2) The pre-shared works when the user is deactivated - persistence
3) The signed request itself works independent of: time skew, body, identity - security bugs
4) You can observe any pre-shared secrets while not logged in as an active user - information disclosure

So if you make sure that the server only sends a pre-shared secret out-of-band or only after active users are logged in (they were actually challenged, not just an active session or cert or token) - the that secret is secure for a browser client, or any client including iOS Apps (which have the same security limitations as a browser, often embed a browser for auth, prove me wrong).

Which should you use?

TL;DR Use HMAC

HMAC is not new, most clouds and SaaS APIs require clients to use it.

HMAC is very simple for a client that has time awareness and the pre-shared secret. The secret acts fundamentally the same as the traditional APIKey in your application code, but a HMAC will not be disclosing the key on every request.

HMAC is really the simplest possible way to secure APIs.

Do I need an OAuth authentication server, or integrate with any third party?

No, If someone is bringing up OAuth when discussing HMAC or JWT, that tells you that they don't know much about HMAC or JWT yet.

To be clear, when the topic is about OAuth itself only, the goal is OAuth and not just HMAC or JWT, then you certainly can include HMAC or JWT as one of the signature options used for OAuth token verification. Because OAuth requires you to verify the identity of the user you need to chose and implement an authentication verification step if you run your own OAuth authentication server, and third parties typically support multiple methods of verification where M/TLS is the newest and HMAC is the oldest, and JWT is the hot/hyped overly complex option.

If you are not wanting or needing OAuth and just want to secure your API with HMAC or JWT, then forget all about OAuth.
As an analogy; talking about OAuth is similar to discussing public transport routes as a group and you were not listening to the question in the first place and blow in later with a car route, after it was very clearly established there is no interest in using a car. Don't be that person..

Python example

Before we look at a solution, can python maintainers please consider including time skew, optionally at least? Making all developers do it in a bespoke disparate way either means they don't bother, or do it poorly, either way, HMAC in python easier to pwn then the rfc indicates it can/should be.

Also, while ranting; FastAPI/Django/Flask may one day get real HMAC support too. I mean real support that doesn't involve developers hard-coding an environment variable for the pre-shared secret/s. This is dumb. That typically means clients all share the same pre-shared secret, or, developers are forced to (again) make their own bespoke disparate HMAC system for the framework instead of using the limited and arguably insufficiently secure version.

Given the limitations, the following Flask route decorator demonstrated the following;

  • Time-based HMAC signature verification using SHA256, SHA512, SHA3-256, SHA3-384, SHA3-512, and BLAKE2
  • Customisation of the not_before or expire_after time-skew per API route, good for allowing synchronisation API endpoints that need to work with offline and aeroplane mode applications and providing allowances for API testing while still blocking replay attacks
  • Supports GET without request data, and other HTTP methods that include a body, both are supported in a single client signing procedure
  • Identity-linked secret token support, no hard coding

You can checkout this Gist; https://gist.github.com/chrisdlangton/11c77772fc0409d4d6d724ea4a6c28bd

Conclusion

Maintainers; let's try to get HMAC right.

If it remains incomplete or provided with insufficient consideration for applications use cases to be implemented securely, if HMAC fails to be ready for secure use, we'll all be forced to use that horrible disparate JWT method and third parties that managed to achieve a SDK that balances security with usability. Regardless of SDK it's undeniable that JWT client implementation is overly complex, and we all know complexity is the nemesis of security.

Stick to HMAC, it is typically safe and very easy to consume already. For examples like python it is fairly straight-forward to add application logic to implement the missing security features too.