API Key Generator
An API key identifies a caller. Unlike a password it is not memorised, not rotated by a human, and frequently pasted into a config file, a CI variable and a colleague's terminal — so the design questions are about handling, not strength.
Prefix Your Keys
The single highest-value design choice, and the most often skipped:
``
sk_live_9f1c8a2e7b3d4f6a9c2e1d8b4a6f3c07
│ │ └─ the random part
│ └────── environment
└───────── key type: secret
`
A prefix buys three things. Secret scanners — GitHub's included — match on known prefixes and
can alert you within minutes of a key being committed. Support can identify a key type from a
screenshot without seeing the secret. And a live key pasted into a test environment fails
loudly instead of charging someone.
Store a Hash, Not the Key
Treat an API key exactly like a password: hash it before storage, show the plaintext once at
creation, and never again. A database leak then exposes hashes rather than working
credentials.
Because keys are high-entropy, a fast hash such as SHA-256 is sufficient — the slow-KDF
argument applies to low-entropy human passwords, not to 128 random bits. Store a short prefix
alongside the hash so the key can be identified in a list.
Scope and Expiry
| Practice | Why |
|---|---|
| One key per integration | Revoke one without breaking the others |
| Least-privilege scopes | A read-only key cannot delete anything |
| Expiry dates | Forces rotation to be a process rather than an incident |
| Last-used timestamps | Reveals which keys can be revoked safely |
| Separate live and test | Prevents the expensive category of mistake |
Rotation Needs Overlap
Rotation only works if two keys can be valid at once — issue the new key, deploy it, confirm
traffic has moved, then revoke the old one. Systems allowing only one active key make
rotation an outage, so nobody rotates.
Keys in URLs Leak
Query strings appear in server logs, proxy logs, browser history and Referer headers.
Send keys in an Authorization header, never as a URL parameter.
The Avalanche Effect
A one-character change produces a completely different digest — not a similar one. That
property is what makes a hash useful as a fingerprint:
| Input | MD5 | CRC32 |
|---|---|---|
hello | 5d41402abc4b2a76b9719d911017c592 | 3610a686 |
hello. | d94c10e437d18531e122ed0b45badd2a | 0a39d4f1 |
Hello | 8b1a9953c4611296a827abf8c47804d7 | f7d18982 |
hello and Hello differ by one bit of one byte, and share no part of their output.
RIPEMD-160 of hello is 108f07b8382412612c048d07d13f814118445acd, and of Hello is
d44426aca8ae0a69cdbc4021c64fa5ad68ca32fe` — same story.Digest Length and Collision Resistance
| Algorithm | Output | Birthday bound | Status |
|---|---|---|---|
| CRC32 | 32 bits | ~77,000 values | Checksum only |
| MD5 | 128 bits | 2⁶⁴ in theory | Broken — collisions in seconds |
| SHA-1 | 160 bits | 2⁸⁰ in theory | Broken — SHAttered, 2017 |
| RIPEMD-160 | 160 bits | 2⁸⁰ | No practical attack |
| SHA-256 | 256 bits | 2¹²⁸ | Current standard |
| SHA-512 | 512 bits | 2²⁵⁶ | Standard, faster on 64-bit |
Never Hash a Password With These
A general-purpose hash is designed to be fast, which is exactly wrong for passwords: speed helps the attacker. Use a deliberately slow KDF — bcrypt, scrypt or Argon2id — with a per-password salt. A GPU tries billions of SHA-256 guesses a second and a few thousand bcrypt guesses a second, and that gap is the entire defence.