
Critique My Plan: API Key for Authentication - rbanffy
https://dev.to/imthedeveloper/critique-my-plan-api-key-for-authentication-26i5
======
tptacek
This developer has pretty much arrived at the right answers by themselves.

 _Key Generation_

generate-safe-id is fine. For a secret API key, UUIDs are less fine. Also
fine: directly reading 16-32 bytes from crypto.getRandomBytes and converting
to hex.

 _Key vs Secret_

An AWS_Access_Key is basically just a random username. If you understand how
userids work in your system, you don't have to worry about this.

 _Key Usage_

Don't use JWT; JWT is a mess. The real distinction here isn't what a secret
key can get you, but whether you attempt to track clients without serverside
state. What JWT ostensibly gets you is an arbitrary per-client state store
without requiring database lookups on the serverside. In reality, you almost
never get that, and inherit all the downsides of JWT --- including extreme,
scary complexity --- for very little practical benefit. If you're already
using a database to handle client request (like most applications), keep it
simple.

 _Key Storage (Clientside)_

Cookies are safer than localStorage; modern browsers are bristling with
defenses for cookies, but not for localStorage access.

 _Key Storage (Serverside)_

We hash passwords because passwords are valuable across sites; it's a big deal
to compromise someone's password, even on a random low-value application.
That's not true of API keys. If your database is compromised, the API keys
don't matter anymore. Don't bother encrypting them.

 _Authorization_

Authorization is a big topic. API keys are an authentication concern. AuthN !=
AuthZ. Keep it simple with AuthN.

 _Don 't:_

* Bother with TLS client certificates. Client certs are useful internally, when you have an ensemble of services that need to talk to each other securely. They're much less useful when you have tens of thousands of semi-anonymous clients. They're a huge pain in the ass to deal with on both the client and the server side, and have extremely bad UX.

* Use JWT or Macaroons. If you can get away with opaque random strings and a serverside database, then get away with that, for as long as you possibly can. We work on applications running at "popular Internet app" scales and they manage this just fine. Trying to push client state to the client _costs_ security; it's something you _get away with_ , not something you do to shore up your defenses.

* Bother with OAuth. The only "interesting" thing OAuth provides is a UX to allow you to delegate authentication to third-party services --- think, every app that wants to see your Twitter timeline. If you don't have that problem, OAuth doesn't buy you anything except complexity.

~~~
BoppreH
I would think that hashing the API keys for storage, like a password, is worth
it. It's extremely cheap in both development and CPU time because you can just
use SHA-256 (high entropy is sufficient to deter brute force), and avoids
unauthorized accesses in case of a database leak.

~~~
tptacek
If you've lost the database, you've lost accountability for all user actions
anyways. Everyone should be forced to reset their credentials.

Meanwhile: storing authenticators of API keys rather than the keys themselves
precludes you from doing "request signing" with the keys in the future, since
validating a MAC will require the preimage of the hash.

Generally: if you're concerned about survivability of systems after
compromise, a better strategy for storing API secrets would be to
compartmentalize them in a simple authentication server with a tiny attack
surface (you could implement it with minimal state by doing a challenge-
response protocol internally, so that the only interface exposed by the
authentication server would be "does this message have a valid MAC").

~~~
BoppreH
> If you've lost the database, you've lost accountability for all user actions
> anyways.

Not sure I understand why. Is it because the (hashed) passwords are there too,
or did you have an attack against the API keys in mind? Just to be clear, I'm
thinking of an unsecured backup scenario, not full blown database compromise.

Regarding signing, that's a good point I hadn't considered, and maybe reason
enough not to hash the API keys.

~~~
tptacek
In my mental model, you lose the database to appsec flaws, not to opsec flaws
with backups. Both things definitely can happen! I'd suggest though that
losing your entire (auth) database to a compromised backup is a "reset all the
API keys" moment anyways.

Remember: these services that provide API keys are invariably backed with some
kind of login service that uses passwords. Even if you're hashing with scrypt,
if you lose the password hashes, everyone's resetting creds.

~~~
ronnier
Isn’t the issue the time when you’ve lost your dB and the time you realize it?
During that time keys could be used.

------
throwanem
I like seeing the "you're struggling toward reinventing JWT; consider just
using JWT" discussion play out in the comments. (Spoiler: the dev ends up
deciding to just use JWT.)

~~~
jchw
JWT is a great idea, but in practice the actual specification is broken in
dumb ways. You may as well lob off the entire header portion of the token
since there's no sense in listening to what it says.

~~~
throwanem
Sure, but if I'm not actually setting out to reinvent auth, I'm still better
off going with a suboptimal but basically functional standard like JWT than I
am trying to roll my own.

~~~
jchw
If you would've used this logic just a little while ago and slapped an off-
the-shelf JWT library into your app, you'd have a trivially exploitable design
flaw that could be used to easily forge tokens.

In that case, reinventing the wheel properly probably would've been more
secure, if not perfect.

I personally use JWT, but unfortunately like so many other things you can't
just drop it in and forget; you have to have some level of care if you really
want it to work. All cryptography has its caveats.

OTOH, going with simple, server-issued tokens that don't carry data actually
is pretty proven already, so it's not really that bad of an idea anyways, if
you are only ever going to have a few servers in the same region anyways.

~~~
davewritescode
There’s so much FUD around JWT that I feel compelled to answer this everytime
it comes up.

Just to get it out of the way, you’re correct that the JWT spec doesn’t do a
lot to prevent implementors from doing stupid things, particularly bad was
downgrade attacks from asymmetric to symmetric keys.

Here’s the thing, these issues don’t exist anymore. Amazon’s Cognito relies
heavily on JWT and I tend to trust Amazon’s security folks.

~~~
jchw
This is not FUD. It is a design flaw.

It is now widely recognized that the header component of the JWT token cannot
be used except to reject a token, making it pretty close to absolutely useless
(except for debugging.)

HOWEVER. Before this was recognized, _most_ JWT implementations were broken
and were easily susceptible to the most basic of downgrade attacks.

Downgrading from asymmetric to symmetric was a slap in the face for obvious
reasons, but that's just the last round of problems. The early problems were
even more ridiculous; many libraries would readily downgrade to 'none' and
turn off protection altogether.

Not just Amazon Cognito, but Google's OIDC implementation, and indeed, all
OIDC implementations, use JWT. JWT implementations today are hopefully no
longer susceptible to basic lapses in security.

But there's good reason for its reputation: the way it's designed lead to
these trivial downgrade attacks. Having the token specify the algorithm was a
bad decision, and it lead to bad implementations. Importantly, someone
implementing JWT today could easily make some of the same mistakes if they
aren't careful.

I hope a future JWT release entirely removes the information from the token
and just forces the client/server to agree statically.

~~~
tptacek
I agree strongly that JWT is badly flawed, and that criticisms of JWT don't
constitute "FUD". JWT's uptake has been alarming given how little
cryptographic engineering input the format seems to have received, versus how
complex it is under the hood.

That said:

Asymmetric crypto is a crypto code smell. You use it when you absolutely have
to because there's no other way to express what you're trying to accomplish.
It is much harder to get public key crypto right than it is to safely use a
"Seal/Unseal" AEAD interface. One of the things that alarms me about JWT is
that it's a format that presumes developers might want to effortlessly switch
between symmetric and asymmetric crypto, as if they were just two different
ways of solving the same underlying problem.

------
Shoothe
I wish the author went deeper into the topic. For example JWTs have a pretty
straightforward structure and if you use just a subset of the spec they look
like a clean and simple solution. But did the author check Macaroons? Did they
consider SSL Client Certificates?

------
Kiro
What is the game?

~~~
milankragujevic
I'd think it's agar.io or slither.io?

------
tinus_hn
What is JWT?

~~~
pc86
The first Google search result for "jwt" is [https://jwt.io/](https://jwt.io/)
which explains it pretty well for you.
[https://en.wikipedia.org/wiki/JSON_Web_Token](https://en.wikipedia.org/wiki/JSON_Web_Token)
is pretty good, too.

------
gcb0
migth want to check out
[https://github.com/yahoo/athenz](https://github.com/yahoo/athenz)

~~~
domainkiller
An auth/security lib from the company that leaked a billion users PII?

------
ataturk
First off, I'm not an expert at security, but I do have a background in it and
in development of microservices. So fair warning.

API keys provide reasonably good security, but there is a reason why companies
have moved to OAuth. Using https, your payloads are protected by SSL, but the
URI and the request and response headers aren't. That means no matter how you
intend to pass along your API key, it is in the open and thus can be
intercepted.

So, bad, right? Well, maybe not. If whatever you are securing with an API key
is not that critical to anyone and you just want to prevent outsiders from
calling your API or calling it repeatedly and abusing it, then API keys are
fine. I would make it hard to guess, but I wouldn't go crazy with that,
either, because everything is "easy to guess" for anyone who is really working
hard at gaining access. If you are just working on a game, I would do the
basics to secure your services and then move on to the more critical matters.

~~~
throwanem
You appear to be shadowbanned for no immediately obvious reason. I vouched
this comment to point out that it contains a factual error, to wit, the claim
that the headers and URI of a request made via TLS are sent in the clear. They
are not; the socket is encrypted before any protocol messages are exchanged.
Perhaps you're thinking of SMTP STARTTLS?

Companies have moved to OAuth, and to JWT as the payload, because it provides
a lightweight, shared-little authentication/authorization framework that's
well suited to the needs of a distributed microservice architecture and to
third-party service integration - not because it overcomes a nonexistent
problem with HTTPS not encrypting headers.

~~~
0x0
A properly configured SMTP STARTTLS would not really send anything substantial
in cleartext either, it would issue the STARTTLS command as the first command
and the rest would be wrapped in encrypted TLS. You can think of the
unencrypted STARTTLS preamble as a set of static ascii bytes that are the same
for every connection.

~~~
throwanem
It's the closest common parallel I can find for what the prior commenter is
describing, but yeah, it isn't really all that close. I've seen poor
implementations leak, but not for many years now.

~~~
bmm6o
I don't think the op had something else in mind. IME it's a strangely
widespread misunderstanding that https encrypts just parts of the http
request. I think it's because of the advice to not put sensitive data in urls.
The reason for that is due to logging at the endpoint, but it's misunderstood
that it's not encrypted. It's funny because it's so easy to test the
hypothesis, and it's hard to even propose a reason it would be that way.

