Generate API keys using crypto/rand to create cryptographically secure random bytes, then encode them as Base64 or Hex strings for storage. Validation requires comparing the incoming key against a stored hash (using bcrypt or argon2) rather than storing the raw key, ensuring that even if your database is compromised, the actual keys remain protected.
Here is a practical implementation using crypto/rand for generation and bcrypt for secure validation:
package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"log"
"golang.org/x/crypto/bcrypt"
)
// GenerateAPIKey creates a 32-byte random key and returns it as a Base64 string.
func GenerateAPIKey() (string, error) {
bytes := make([]byte, 32)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
// Use Base64 for a compact, URL-safe representation
return base64.StdEncoding.EncodeToString(bytes), nil
}
// HashAPIKey hashes the raw key for secure storage.
func HashAPIKey(key string) ([]byte, error) {
// bcrypt handles salting internally
return bcrypt.GenerateFromPassword([]byte(key), bcrypt.DefaultCost)
}
// ValidateAPIKey checks the raw key against the stored hash.
func ValidateAPIKey(key string, storedHash []byte) bool {
err := bcrypt.CompareHashAndPassword(storedHash, []byte(key))
return err == nil
}
func main() {
// 1. Generate a new key
rawKey, err := GenerateAPIKey()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Generated Key: %s\n", rawKey)
// 2. Hash it for storage (simulate DB save)
hashedKey, err := HashAPIKey(rawKey)
if err != nil {
log.Fatal(err)
}
// 3. Validate a request (simulate incoming API call)
isValid := ValidateAPIKey(rawKey, hashedKey)
fmt.Printf("Validation Result: %v\n", isValid)
// 4. Validate a wrong key
wrongKey := "invalid-key-string"
isValidWrong := ValidateAPIKey(wrongKey, hashedKey)
fmt.Printf("Wrong Key Validation: %v\n", isValidWrong)
}
In production, never store the raw API key in your database. Always store the bcrypt hash. When a client sends a key in the Authorization header, retrieve the corresponding hash from your database and run ValidateAPIKey. This approach prevents rainbow table attacks and ensures constant-time comparison to mitigate timing attacks. If you need to revoke keys, simply delete the hash record or add a revoked_at timestamp to your database schema.