This post originated from an RSS feed registered with Ruby Buzz
by Jan Lelis.
Original Post: Ruby and Random
Feed Title: rbJ*_*L.net
Feed URL: http://feeds.feedburner.com/rbJL
Feed Description: Hi, I am a fan of Ruby and like to explore it and the world around ;).
So I started this blog, where I am publishing code snippets, tutorials for beginners as well as general thoughts about Ruby, the web or programming in general.
Wrongly used randomness can be the source of hard-to-detect bugs and security holes. This is relevant every time you use randomness, for example, when implementing an existing protocol/interface that requires random values or generating tokens for your next raffle. This article describes when to use which of Ruby’s randomness methods.
Don’t use or rely on , because of its global state. Generally use SecureRandom, which is suitable for cryptographic purposes! If you need reproducible randomness, use your own Random object!
Basic Ways to use Randomness in Ruby
Generating random values in Ruby is convenient:
rand 10 # integer between 0..9
rand 2..5 # integer between 2..5
rand # float between 0 and 1
Random.rand # basically the same like rand
Select Array Elements
[1,2,3,4,5].sample # picks one element
[1,2,3,4,5].sample 3 # picks a subset of three elements
[1,2,3,4,5].shuffle # reorders the array
But all the above methods have one thing in common: They depend on the same global seed. The seed is a non-random number needed to initialize the pseudorandom number generator used by Ruby (a Mersenne Twister). These number generators work pretty well (they are not unbreakable, though). The seeds are typically made out of always changing numbers like the process number and the current time. The global seed is created when the Ruby process starts.
However, it’s also possible to set your own seed using the or the Random. method. You should not do this. Furthermore, you should not rely on methods that depend on at all, because the seed is a global state and gets shared between all libraries loaded.
Because of Ruby’s nature, it’s not uncommon that a single library can change (and potentially break) the behavior of a Ruby application. But you should consider that the use of (instead of the newer approach below) could have happened without malicious intentions, which still might open a door for attackers. If your application uses any -based method (e.g. Array) and you care about the quality of your randomness, you will have to check all the gems in your Gemfile.lock for the use of .
By the way, another feature of is that it returns the previously used seed. This might be useful to calculate a new seed based on the old one. On the other hand, it (theoretically) allows an attacker to “steal” your previously used randomness:
$ irb -f
>> def generate_secret_token
>> end # => nil
>> File.write 'secret_token_file', generate_secret_token # writes e.g. "5^hh}kq&294']rQ.|"
>> # other stuff is going on
>> # ...
>> # evil coworker accesses your irb
>> srand srand # capture old seed and use it again
>> generate_secret_token # => "a5^hh}kq&294']rQ."
The Proper Ways to use Randomness in Ruby
This should be your favorite way of creating a random number:
It also provides the useful and methods. SecureRandom works different than the usual randomness methods, since it uses external libraries to generate the randomness. At first, it tries to use OpenSSL and if that’s not available, it will try /dev/urandom or advapi32, which are provided by your operating system. It does not have the problem and is considered to be strong enough for cryptographic purposes. So why not use it everywhere?
Sometimes, you face a different problem: You might want to generate a random sequences that can be reproduced later. You don’t need to use to achieve this, anymore. Newer Ruby versions fix this global state problem by including the Random class that allows you to create instances that represent the state of the prng in an object oriented way. It is used like this:
r = Random.new
Pay attention to not do [1,2,3,4,5].shuffle(random: Random.new) – Creating new seeds all the time is less secure than using new numbers out of an existing prng. In these cases, you should use SecureRandom (see above) instead.