Plate 09
secrets vs urandom vs getrandbits: Localhost Lab
Aditya Challa4 min read
Intro — what this post promises
How fast can you mint random bytes — secrets.token_bytes, os.urandom, or random.getrandbits → bytes? This lab reports MB/s (and ops/s for small sizes) on Linux localhost.
It is not a remake of the uuid4 vs secrets token localhost lab (UUID/token formatting ops/s). Here the focus is bulk byte generation and the crypto vs non-crypto fork: use secrets / os.urandom for security-sensitive material; use random only for simulations / fuzz / non-secret noise.
Related links:
- uuid4 vs secrets token localhost lab
- hashlib md5 vs blake2b localhost lab
- bytesio vs spooled tempfile localhost lab
- weakref vs dict cache localhost lab
- pickle vs json roundtrip localhost lab
- decimal vs float sum localhost lab
- bytes vs bytearray localhost lab
- csv reader vs split localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. No Docker. Not a cryptographic proof — throughput + clear API guidance only.
Verdict up front (1 MiB): secrets.token_bytes ~552.7 MB/s ≈ os.urandom ~551.9 MB/s; chunked getrandbits ~48.5 MB/s (~11.39× slower). At 16 B tokens, urandom ~3.32 Mops/s beat secrets ~1.46 Mops/s — still use secrets for API clarity on security paths.
Arms
| Arm | Role |
|---|---|
secrets.token_bytes(n) | CSPRNG wrapper — preferred for secrets |
os.urandom(n) | same OS entropy; lower-level |
random.getrandbits chunked/oneshot | MT / non-crypto — simulations only |
Lab topology
Script: lab-evidence/88-secrets-vs-urandom/results/run_lab.py.
Lead table — bulk (p50 MB/s)
| Size | secrets | os.urandom | getrandbits chunked |
|---|---|---|---|
| 1 KiB | 451.7 | 461.7 | 63.0 |
| 64 KiB | 546.3 | 552.8 | 62.7 |
| 1 MiB | 552.7 | 551.9 | 48.5 |
Oneshot getrandbits(n*8).to_bytes(...) at 64 KiB hit ~492.3 MB/s — closer to CSPRNG speed, still not suitable for secrets.
Small tokens (ops/s)
| Size | secrets | os.urandom | getrandbits oneshot |
|---|---|---|---|
| 16 B | 1461976 | 3322269 | 2617834 |
| 32 B | 1550389 | 2008020 | 2493766 |
Reading it
secrets≈os.urandomfor bulk on this host (~550 MB/s MiB) — secrets is the right default API for passwords/tokens/keys.random.getrandbitsis for games/sim, and naive 8-byte chunked packing was ~11.39× slower than CSPRNG here — oneshot closes the gap but does not become cryptographically safe.- Do not pick
randombecause a microbench looked hot at 16 B.
Security framing (short)
| Need | Use |
|---|---|
| Session IDs, API keys, CSRF, password salts | secrets (or os.urandom) |
| Shuffle a playlist / Monte Carlo | random |
| “Looks random” test fixtures | either; prefer secrets if fixtures ever leak into prod auth |
Differentiation from lab 41
Lab 41 compared uuid4 vs secrets.token_hex / token_urlsafe mint rates. This lab streams raw bytes across sizes and includes random.getrandbits, with an explicit non-crypto warning.
Bulk vs token minting
At 16–32 B the metric that matters is ops/s (auth cookies, CSRF). At ≥1 KiB, MB/s matters (key material, padding, test payloads). On this box both CSPRNG APIs converge near ~550 MB/s once buffers are large; the interesting fork is whether random is allowed at all — for secrets, no.
Pitfalls
- Using
randomfor tokens because it is “fast enough”. - Building bytes with many tiny
getrandbitscalls (chunked tax). - Assuming MB/s equality means API indifference — prefer
secretsin security code for intent and reviewability. - Seeding
randomand thinking that makes it secure.
Reproduce
Evidence: summary.json, summary.txt.
Limits
One Linux box (/dev/urandom backed). Not a NIST CSPRNG audit. Free-threaded / alternate RNG builds may differ.
Takeaway
For bulk CSPRNG bytes, secrets.token_bytes ~552.7 MB/s ≈ os.urandom. For non-crypto noise, getrandbits is fine — but never for secrets. Prefer secrets in security paths even when urandom wins a few ops/s at 16 B.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5. 1MiB: secrets 552.7 MB/s ≈ urandom 551.9; getrandbits chunked 48.5. 16B: secrets 1461976/s vs urandom 3322269/s. random NOT for secrets. Differs from lab 41 uuid framing. Affiliates: 0. Evidence: lab-evidence/88-secrets-vs-urandom.
Related links
Plate 12
islice vs list Slice Windows: Localhost Lab
Hands-on itertools.islice vs list slice window lab: real ops/s taking ranges from sequences, measured on Linux localhost in this hands-on lab for SREs.
Observability & SRE · 1 Oct 2026
Plate 07
heapq.merge vs sorted(chain): Localhost Lab
Hands-on heapq.merge vs sorted(chain) multi-way merge: real records/s on pre-sorted lists, measured on Linux localhost today in this hands-on lab for SREs.
Observability & SRE · 1 Oct 2026
Plate 88
mmap Write vs pwrite Region: Localhost Lab
Hands-on mmap MAP_SHARED write+msync vs pwrite region update: real MB/s with durability labels, measured on Linux localhost in this hands-on lab for SREs.
Observability & SRE · 1 Oct 2026