Skip to main content
Unkey stores every API key as a SHA-256 hash of the full key string, standard base64, with no salt. Verification hashes the presented key and compares the hashes. Unkey doesn’t keep the plaintext unless you turn on recovery for that key. For background, see the OWASP Secrets Management Cheat Sheet and the OWASP REST Security Cheat Sheet. SHA-256 here isn’t a password hash. Recoverable keys let you show a key again after it’s created. That comes with extra risk, so turn recovery on only when you need it: a dashboard that must show a key again, a migration that moves keys, or a support flow that reads a key back for a customer. By default you see a key only once, when it’s created, and that’s the right setup for most products. For the steps, see Recoverable keys.

How the encrypted copy is stored

A recoverable key is still verified by its hash. Beside the hash, Unkey stores an AES-256-GCM ciphertext of the key. The data key is 32 bytes, the nonce is 12 bytes, and the keyring is the workspace, so another workspace can’t decrypt it. keys.getKey and apis.listKeys decrypt it only when you pass decrypt: true, and only with a root key that has decrypt_key. A key is only recoverable if its keyspace has Store encrypted keys turned on (ask support@unkey.com) and the key was created with recoverable: true after that. Older keys can’t be recovered.

Two separate permissions

Creating a recoverable key needs api.*.encrypt_key or api.<api_id>.encrypt_key on top of create_key. Reading a key back needs api.*.decrypt_key or api.<api_id>.decrypt_key on top of read_key. Without decrypt_key, a root key can create and list keys but never read them back. Give decrypt_key only to the one service that needs to show keys, for the one keyspace that needs it.

Limiting the exposure

With recovery on, a copy of our database is no longer useless on its own. Someone who also got your workspace’s encryption keys, or a leaked root key with decrypt_key, could read your keys. To limit that:
  • Only turn on recovery for keyspaces that need it.
  • Never pass decrypt: true in code that serves end users.
  • Use the developer portal, where users roll their own keys instead of reading them back.
  • Watch the audit log for unexpected key.create entries in keyspaces with encryption on.
If rerolling meets your need, use it instead. The user gets a fresh key and nobody reads the old one.
Last modified on October 11, 2026