ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 09

  1. Blog
  2. /Observability & SRE

secrets vs urandom vs getrandbits: Localhost Lab

Aditya Challa·30 September 2026·4 min read

Lab
On this page
  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — bulk (p50 MB/s)
  5. Small tokens (ops/s)
  6. Reading it
  7. Security framing (short)
  8. Differentiation from lab 41
  9. Bulk vs token minting
  10. Pitfalls
  11. Reproduce
  12. Limits
  13. Takeaway

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

ArmRole
secrets.token_bytes(n)CSPRNG wrapper — preferred for secrets
os.urandom(n)same OS entropy; lower-level
random.getrandbits chunked/oneshotMT / non-crypto — simulations only

Lab topology

sizes: 16 B / 32 B / 1 KiB / 64 KiB / 1 MiB · 7 rounds · p50
metric: MB/s = nbytes / p50_s ; ops/s for small tokens

Script: lab-evidence/88-secrets-vs-urandom/results/run_lab.py.


Lead table — bulk (p50 MB/s)

Sizesecretsos.urandomgetrandbits chunked
1 KiB451.7461.763.0
64 KiB546.3552.862.7
1 MiB552.7551.948.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)

Sizesecretsos.urandomgetrandbits oneshot
16 B146197633222692617834
32 B155038920080202493766

Reading it

  • secrets ≈ os.urandom for bulk on this host (~550 MB/s  MiB) — secrets is the right default API for passwords/tokens/keys.
  • random.getrandbits is 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 random because a microbench looked hot at 16 B.

Security framing (short)

NeedUse
Session IDs, API keys, CSRF, password saltssecrets (or os.urandom)
Shuffle a playlist / Monte Carlorandom
“Looks random” test fixtureseither; 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 random for tokens because it is “fast enough”.
  • Building bytes with many tiny getrandbits calls (chunked tax).
  • Assuming MB/s equality means API indifference — prefer secrets in security code for intent and reviewability.
  • Seeding random and thinking that makes it secure.

Reproduce

python3 lab-evidence/88-secrets-vs-urandom/results/run_lab.py

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.

secrets.token_bytesos.urandomrandom.getrandbitscsprngrandom byteslocalhost labsrepython secrets

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.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

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

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — bulk (p50 MB/s)
  5. Small tokens (ops/s)
  6. Reading it
  7. Security framing (short)
  8. Differentiation from lab 41
  9. Bulk vs token minting
  10. Pitfalls
  11. Reproduce
  12. Limits
  13. Takeaway
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove