Random numbers, ids and tokens

Random with a seed

A Random object with a seed produces the same sequence every time it is created with that seed. That is what a test or a simulation wants: a repeatable shuffle, not a surprise.

ruby -e 'r = Random.new(42); puts r.rand(100); r2 = Random.new(42); puts r2.rand(100)'

51
51

The top-level rand uses one shared generator and is seeded from the clock, so two runs differ. rand with a range and shuffle take the same seed idea when they are called on the object: r.rand(1..6), r.shuffle(names).

SecureRandom for tokens

A seeded generator is predictable by design, which makes it the wrong tool for a password reset link, an API key or a session id. The securerandom library draws from the operating system and is the one to use for anything a person must not guess:

ruby -e 'require "securerandom"; puts SecureRandom.uuid'

77090d45-73f6-4472-8fce-29fa49018570

ruby -e 'require "securerandom"; puts SecureRandom.hex(8)'

a5250daba79d920f

uuid gives the usual 36-character identifier; hex(8) gives 16 hex characters, one byte per two. urlsafe_base64(16) gives 22 characters that can sit in a URL without escaping.

What to watch for

Seeding a generator for a test is good; seeding it for a token is a vulnerability. If a value protects something, it comes from SecureRandom, never from rand.

A twelve-character hex token is 48 bits, which is plenty for a one-time link and not enough for a long-lived key. Pick the length by what the token protects, and store a hash of it rather than the token itself.

Next: Conditions

Article author: Arthur Isaev

Banner image