Audience: Support, Customers, and Operators
Summary (TL;DR):
- Provisioner keys are 43-character, random and derived from a character set of 62 characters (a-z, A-Z and 0-9).
- Keys are generated with a cryptographically secure RNG (OS CSPRNG). This is
/dev/urandomon Linux,arc4randomon MacOS andCryptGenRandomon Windows. - Coder stores only the SHA-256 hash of the key. It doesn't store the plaintext.
How key generation works:
- Secret creation: This happens within the file linked here. The most relevant code snippet can be found below:
const (
secretLength = 43
)
func New(organizationID uuid.UUID, name string, tags map[string]string) (database.InsertProvisionerKeyParams, string, error) {
secret, hashed, err := apikey.GenerateSecret(secretLength)
if err != nil {
return database.InsertProvisionerKeyParams{}, "", xerrors.Errorf("generate secret: %w", err)
}
if tags == nil {
tags = map[string]string{}
}
return database.InsertProvisionerKeyParams{
ID: uuid.New(),
CreatedAt: dbtime.Now(),
OrganizationID: organizationID,
Name: name,
HashedSecret: hashed,
Tags: tags,
}, secret, nil
}As we can see, We have a 43 character random string which is generated and returned along with the SHA-256 hash of the secret. ONLY the hashed secret is stored within the database as can be seen from the last part of the above code snippet.
- The random string generation happens within the file linked here. We make use of Go's crypto/rand package and the character set contains 62 characters (a-z, A-Z and 0-9) which is 26 + 26 + 10 = 62.
- We hash the secret here and we use the industry grade SHA-256 hashing algorithm. Coder stores only the SHA-256 hash (plus metadata like organization, name, tags). The plaintext key is returned once at creation time and is never stored.
- On use, the presented key is hashed with SHA-256 and compared to the stored hash using a constant-time comparison to prevent timing attacks.
Why 43 characters?
Search space / Number of possible keys = 62^43 = 1.18 * 10^77 - (We have 62 characters that need to be filled in 43 spots)
Entropy: log base 2 (62^43) = 43 log base 2(62) = 43 * 5.954 = 256.022 (256 bits)
That’s comparable to a 256-bit symmetric key and provides a very large safety margin against brute-force attacks.
Randomness & Uniformity
CSPRNG sources: The generator draws from the operating system’s secure facilities (e.g.,
/dev/urandomon Linux,arc4randomon macOS, and Windows CNG).Unbiased selection: Characters are produced with an unbiased mapping (rejection-sampling approach inspired by Lemire’s algorithm), ensuring every symbol in the 62-character charset is equally likely.
CLI Example
$ coder provisioner keys create my-key
Successfully created provisioner key "my-key"! Save this authentication
token, it will not be shown again.
Psdlt0tkyPOSSsQ77G5WTSwH4zXY8JQFisadNNU3BJV (This is an example key)
Note: The key is shown once. Treat it like a password. If lost, create a new key (the original cannot be recovered).This satisfies the theory laid down above.