Every application needs secrets: API keys, session tokens, encryption keys, database passwords. The way you generate them determines whether they can be guessed, brute-forced, or cracked. This guide explains what makes a secret truly secure and how to generate the right type for each use case.
Why Math.random() Is Not Secure
Math.random() is a pseudorandom number generator (PRNG) seeded from a deterministic algorithm. It's fast and sufficient for games and simulations, but cryptographically it's predictable: given enough output values, an attacker can reconstruct the seed and predict all future values. For security tokens, that means your "random" API key can be computed.
The correct choice is a cryptographically secure PRNG (CSPRNG) seeded from OS entropy (hardware events, interrupt timing, etc.). In the browser, that's crypto.getRandomValues(). In Node.js, it's crypto.randomBytes(). Our secret generator uses the Web Crypto API — the same entropy source your OS uses for TLS key generation.
Understanding Entropy
Entropy measures unpredictability in bits. A secret with N bits of entropy has 2^N possible values. For reference:
- 64 bits — Weak by modern standards. Feasible for well-funded attackers.
- 128 bits — Good. Industry standard minimum for session tokens.
- 256 bits — Excellent. Standard for encryption keys. Computationally infeasible to brute force.
- 512 bits — Overkill for most uses. Appropriate for long-lived root keys.
The entropy meter in our Secret Generator shows bits of entropy and estimated crack time to help you choose the right length.
Which Mode to Use
Base64 — For API Keys and Tokens
32 random bytes encoded in Base64 gives you 256 bits of entropy in a 44-character URL-safe string. This is the standard format for API keys, OAuth client secrets, and session tokens. When in doubt, use Base64 mode at 32 bytes.
UUID v4 — For Database IDs
UUID v4 is 128 bits of randomness in the standard xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx format. It's used as a primary key when you need globally unique IDs without a central sequence. The collision probability for 1 billion UUIDs is roughly 1 in 2.7 × 10^11 — negligible for any real application.
Passphrase — For Human-Memorable Passwords
Diceware-style passphrases (e.g., correct-horse-battery-staple) combine high entropy with human readability. A 4-word passphrase from a 7,776-word list gives 51 bits of entropy. At 6 words, you get 77 bits — stronger than most random passwords while being far easier to remember and type.
JWT Secret — For Auth Signing Keys
For HMAC-SHA256 JWT signing, you need at least 256 bits (32 bytes). JWT mode generates a correctly sized Base64url-encoded secret, ready to paste into .env as JWT_SECRET.
Where to Store Secrets
Never in source code — even private repos. Git history is forever. Never in client-side JavaScript. Here's the right place for each environment:
- Local development:
.envfile (add to.gitignore) - CI/CD: GitHub Actions Secrets, GitLab CI Variables, CircleCI Environment Variables
- Cloud production: AWS Secrets Manager, GCP Secret Manager, Azure Key Vault
- Platform: Vercel/Railway/Render Environment Variables (encrypted at rest)
- Self-hosted: HashiCorp Vault, Infisical, Doppler
Secret Rotation Best Practices
Even a perfectly generated secret becomes a liability if it's never rotated. Build rotation into your system from day one:
- Rotate API keys every 90 days at minimum; JWT signing keys every 30 days
- Invalidate old tokens using a key version/ID in the JWT header or a revocation list
- Use JWKS endpoints for asymmetric keys so rotation doesn't require a redeployment
- Log all key usage and alert on anomalous patterns before rotation to ensure detection
Generate a cryptographically secure secret now:
Open Secret Generator →