ASVS 5.0 V6.5.3 requires that lookup secrets, out of band authentication codes and TOTP seeds are generated with a cryptographically secure pseudorandom number generator, so the values cannot be predicted. This is a Level 2 requirement.
Why it matters
General purpose random functions are built for speed and spread, not secrecy. Their output can be reconstructed from a few observed values. If an attacker sees two or three of your six digit codes, a predictable generator lets them compute the next one for another user. A TOTP seed made from a weak source can be guessed outright, which defeats the factor before it is ever used.
How to implement it
Use the operating system backed generator provided by your language, never the default one.
# Python
import secrets
code = f"{secrets.randbelow(10**6):06d}"
seed = secrets.token_bytes(20) # 160 bit TOTP seed
// Node.js
import { randomInt, randomBytes } from "node:crypto";
const code = String(randomInt(0, 1_000_000)).padStart(6, "0");
const seed = randomBytes(20);
// Java
SecureRandom rng = new SecureRandom();
String code = String.format("%06d", rng.nextInt(1_000_000));
byte[] seed = new byte[20];
rng.nextBytes(seed);
// Go
n, _ := rand.Int(rand.Reader, big.NewInt(1000000)) // crypto/rand
code := fmt.Sprintf("%06d", n.Int64())
Avoid modulo on random bytes, which biases results. Use the range helpers shown above. Seed a TOTP with at least 128 bits, and 160 bits matches the common authenticator apps.
How to test it
- Grep the code that creates codes and seeds for
Math.random,random.random,java.util.Random,rand()andmath/rand. - Run a static scan that flags insecure random use in security contexts.
- Generate a large sample and confirm no repeats and a flat digit distribution. This will not prove strength but will catch obvious mistakes.
Common mistakes
- Seeding a normal generator with the current time.
- Deriving codes from user ID, timestamps or counters.
- Using a UUID version 1 or a sequential ID as a token.
Try it yourself
Static analysis finds weak random sources in code. Run a scan and see what it reports.
Click through it at your own pace, or open it full screen.
Quick check
Is Python's random.randint acceptable for a six digit code?
No. It is not cryptographically secure. Use the secrets module.
Why not use modulo on a random byte?
When 256 is not divisible by the range, some outputs occur more often, which gives an attacker better odds.