August 7th, 2007
Security of Ruby’s Kernel#rand
Last night I was at the Chicago Area Ruby Group, and there was a presentation by Trevor Turk on his El Dorado project. While he was showing us the code I saw the method that generates the application’s authentication token. I could not help but notice that the security of the authentication tokens depends greatly on the security of Ruby’s Kernel#rand method. Traditionally default rand functions are a bit light on security, and so I wondered if that was also true of Ruby’s Kernel#rand.
Only one way to find out! I downloaded a fresh copy of the source code for Ruby 1.8.6 and started poking around in random.c, where the function in question lives. My findings are mixed, and I have good news and bad news to report!
The good news is that if the Unix character device /dev/urandom is available then the random number generator is seeded with a value based from a read from that device. The /dev/urandom device is a “secure” pseudo-random number generator device, so the resulting random numbers are going to be pretty good. Note that you are still at the mercy of how well your operating system implements the algorithm for /dev/urandom and how that number generator gets seeded, but generally it works pretty well.
The bad news is that if /dev/urandom does not exist then your random numbers come from a podge of the current time, process id, and a sequence. That will get you a random number good enough for shuffling your deck of cards in a friendly game of Euchre, but you do not want to depend on it for your bank account’s security.
Should you use Kernel#rand on a system without /dev/urandom? That depends. How secure do you want your application to be? If there is any level of money or privacy involved, then I think you better make sure Ruby can find your /dev/urandom device. You also better verify that your operating system’s /dev/urandom algorithm is implemented well.
What if there are large amounts of money involved? All I have spoken of so far is the security of the seed to the random number generator. How good is the random number generator itself? For high security applications, does it matter how good our seed value is if we fail to use a good algorithm to crunch it?
Once again there is good news and bad news on that front. Ruby uses the Mersenne Twister random number algorithm developed in 1997 by Makoto Matsumoto. Wouldn’t you know it that a Japanese algorithm is in use in a programming language from the same islands? 😉 The bad news is that it is not an algorithm that is naturally suited for secure applications—speed of number generation is its primary focus. After being seeded the number generator will fall into a pattern. The good news is that you need to generate lots of numbers before that pattern emerges, and it turns out that it can be used in secure applications with some limitations. Please note that I am not a cryptographer, so I am gleaning this information from an external source or two. Use it at your own risk.
But before you run out and use Kernel#rand on your new banking application, heed one more note of caution. High security applications should not be depending on /dev/urandom for seeding another random number generator. If you are going to be feeding a random number generator, why not get a real random number? Systems that have /dev/urandom also have a /dev/random. This latter device generates random numbers that do not get filtered through a random number generator. Assuming your system has a good means of obtaining random bits to regurgitate to /dev/random and your application does not consume them too quickly (reads on /dev/random will block if it runs out of bits), you should seed your random number generator directly from /dev/random for your highest security applications.