What's the best approach for generating a new API key?

95,763

Solution 1

Use a random number generator designed for cryptography. Then base-64 encode the number.

This is a C# example:

var key = new byte[32];
using (var generator = RandomNumberGenerator.Create())
    generator.GetBytes(key);
string apiKey = Convert.ToBase64String(key);

Solution 2

API keys need to have the properties that they:

  • uniquely identify an authorized API user -- the "key" part of "API key"
  • authenticate that user -- cannot be guessed/forged
  • can be revoked if a user misbehaves -- typically they key into a database that can have a record deleted.

Typically you will have thousands or millions of API keys not billions, so they do not need to:

  • Reliably store information about the API user because that can be stored in your database.

As such, one way to generate an API key is to take two pieces of information:

  1. a serial number to guarantee uniqueness
  2. enough random bits to pad out the key

and sign them using a private secret.

The counter guarantees that they uniquely identify the user, and the signing prevents forgery. Revocability requires checking that the key is still valid in the database before doing anything that requires API-key authorization.

A good GUID generator is a pretty good approximation of an incremented counter if you need to generate keys from multiple data centers or don't have otherwise a good distributed way to assign serial numbers.


or a hash of a random string

Hashing doesn't prevent forgery. Signing is what guarantees that the key came from you.

Solution 3

I use UUIDs, formatted in lower case without dashes.

Generation is easy since most languages have it built in.

API keys can be compromised, in which case a user may want to cancel their API key and generate a new one, so your key generation method must be able to satisfy this requirement.

Solution 4

If you want an API key with only alphanumeric characters, you can use a variant of the base64-random approach, only using a base-62 encoding instead. The base-62 encoder is based on this.

public static string CreateApiKey()
{
    var bytes = new byte[256 / 8];
    using (var random = RandomNumberGenerator.Create())
        random.GetBytes(bytes);
    return ToBase62String(bytes);
}

static string ToBase62String(byte[] toConvert)
{
    const string alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    BigInteger dividend = new BigInteger(toConvert);
    var builder = new StringBuilder();
    while (dividend != 0) {
        dividend = BigInteger.DivRem(dividend, alphabet.Length, out BigInteger remainder);
        builder.Insert(0, alphabet[Math.Abs(((int)remainder))]);
    }
    return builder.ToString();
}

Solution 5

Update, in Chrome's console and Node.js, you can issue:

crypto.randomUUID()

Example output:

'4f9d5fe0-a964-4f11-af99-6c40de98af77'

Original answer (stronger):

You could try your web browser console by opening a new tab, hitting CTRL + SHIFT + i on Chrome, and then entering the following immediately invoked function expression (IIFE):

(async function (){
  let k = await window.crypto.subtle.generateKey(
    {name: "AES-GCM", length: 256}, true, ["encrypt", "decrypt"]);
  const jwk = await crypto.subtle.exportKey("jwk", k)
  console.log(jwk.k)
})()

Example output:

gv4Gp1OeZhF5eBNU7vDjDL-yqZ6vrCfdCzF7HGVMiCs

References:

https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey

https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/exportKey

I'll confess that I mainly wrote this for myself for future reference...

Share:
95,763

Related videos on Youtube

Phill
Author by

Phill

Kiwi living in New Zealand, getting bored of living where I was born. Kiwi living in Australia, showing Australians what real work is. Kiwi living in Singapore, trying to find time to do anything. Kiwi living in Taiwan, getting morbidly obese.

Updated on March 16, 2022

Comments

  • Phill
    Phill about 2 years

    So with lots of different services around now, Google APIs, Twitter API, Facebook API, etc etc.

    Each service has an API key, like:

    AIzaSyClzfrOzB818x55FASHvX4JuGQciR9lv7q

    All the keys vary in length and the characters they contain, I'm wondering what the best approach is for generating an API key?

    I'm not asking for a specific language, just the general approach to creating keys, should they be an encryption of details of the users app, or a hash, or a hash of a random string, etc. Should we worry about hash algorithm (MSD, SHA1, bcrypt) etc?

    Edit: I've spoke to a few friends (email/twitter) and they recommended just using a GUID with the dashes stripped.

    This seems a little hacky to me though, hoping to get some more ideas.

  • Edward Brey
    Edward Brey almost 10 years
    Do not assume that UUIDs are hard to guess; they should not be used as security capabilities (UUID spec RFC4122 section 6). An API key needs a secure random number, but UUIDs are not securely unguessable.
  • Edward Brey
    Edward Brey almost 10 years
    Windows 2000 switched to GUIDs using random numbers. However, there is no guarantee that the random numbers can't be predicted. For example, if an attacker creates several API keys for himself, it may be possible to determine a future random number used to generate another user's API key. In general, do not consider UUIDs to be securely unguessable.
  • sappenin
    sappenin about 9 years
    Is the signing step of your algorithm necessary if the API key presented by a client is checked against a database of already registered API keys on the server providing the API? Seems like signing would be redundant here if the server is the one providing keys.
  • Mike Samuel
    Mike Samuel about 9 years
    @sappenin, Yes. If you store an unguessable key on the server, then you don't need to prevent forgery. Often API requests are handled by any one of a farm of machines -- the server is one of many servers. Signature checking can be done on any machine without a round-trip to a database which can avoid race conditions in some cases.
  • Micro
    Micro about 8 years
    @EdwardBrey what about UUID uuid = UUID.randomUUID(); in Java? Are you saying that random is not good enough?
  • Edward Brey
    Edward Brey about 8 years
    @MicroR A random UUID is secure only if the random number generator used to make it is cryptographically secure and 128 bits are sufficient. Although the UUID RFC does not require a secure random number generator, a given implementation is free to use one. In the case of randomUUID, the API docs specifically state that it uses a "cryptographically strong pseudo random number generator". So that particular implementation is secure for a 128-bit API key.
  • Abhyudit Jain
    Abhyudit Jain about 7 years
    @MikeSamuel if API key is signed and you don't do a round trip to Database then what happens when the key is revoked but still used to access the API?
  • Mike Samuel
    Mike Samuel about 7 years
    @AbhyuditJain, In any distributed system, you need a consistent message order (revocations happen-before subsequent uses of revoked credentials) or other ways to bound ambiguity. Some systems don't round-trip on every request -- if a node caches the fact that a key was in the database for 10 minutes, there's only a 10 min. window in which an attacker can abuse a revoked credential. Possible confusion can result though: user revokes a credential, then tests that it's revoked, and is surprised because non-sticky sessions cause the two requests to go to different nodes.
  • James Wierzba
    James Wierzba about 7 years
    This is not very secure, an attacker that gains access to your database could obtain the key. It would be better to generate the key as a hash of something unique to the user (like a salt), combined with a server secret.
  • Edward Brey
    Edward Brey about 7 years
    Storing a randomly generated API key has the same security characteristics as storing a hashed password. In most cases, it's fine. As you suggest, it is possible to consider the randomly generated number to be a salt and hashing it with a server secret; however, by doing so, you incur the hash overhead on every validation. There is also no way to invalidate the server secret without invalidating all API keys.
  • Dawid Ohia
    Dawid Ohia about 6 years
    Nice solution, but you need var keyword before apiKey probably :) var apiKey = Convert.ToBase64String(key);
  • Edward Brey
    Edward Brey about 6 years
    @JohnM2 Right. I had been leaving it open-ended, since apiKey could be declared elsewhere. I added the type for clarity.
  • chrisinmtown
    chrisinmtown almost 6 years
    I hope this is a sensible question: do you recommend storing a one-way hash of a generated apiKey in the database, just as is done for user-supplied passwords? Then an adversary that reads the database still cannot easily impersonate the actual user AFAIK.
  • Edward Brey
    Edward Brey almost 6 years
  • Jon Story
    Jon Story almost 4 years
    @JamesWierzba if the attacked is already in your database, then them having unsecured access to your API is probably the least of your concerns...
  • João dos Reis
    João dos Reis almost 4 years
    @EdwardBrey not quite the same characteristics. Someone who reads the database with the API key in it now has a valid API key. Someone who reads a hashed password cannot use that hash as a password.
  • Edward Brey
    Edward Brey almost 4 years
    @RobGrant You can use a hashed password by writing a fake app that pretends to be the real app. Whereas the real app would hash the user's password, the fake app just uses the pre-hashed password.