Andre's Blog
Perfection is when there is nothing left to take away
Random security

Generating good random values is critical for security of applications using them. Unfortunately, far more critical than many developers realize, as was highlighted by the recent flurry of DNS servers reported vulnerable to various forms of attacks based of predictability of their transaction identifiers.

Good seed, good crop

Sometimes developers do not have control over the calling thread (e.g. web server request processing) and cannot seed the random number generator at the time the thread was started. Many then choose to implement a simple work-around by seeding the random number generator immediately before using it.

For example, consider code that generates a temporary password that can be emailed to a user so they can reset their lost password. Note that srand() is called right before password characters are picked from the array.

static const char alphabet[] = "!\"#$%&'()*+,-./0123...xyz{|}~";
char password[17] = {0};
srand(time(NULL));
for(int i = 0; i < 16; i++)
   password[i] = alphabet[rand()/(RAND_MAX/(sizeof(alphabet)-2))];

Password generated by such code could easily be guessed if server time is known, which is conveniently available in any HTTP response generated by most web servers. All a hacker would need to do in order to obtain a password is to request a password reset on somebody's behalf, use the time returned by the web server to seed the random number generator and use the temporary password to login and change the password to anything they want.

Good uncertainty

In the world of security uncertainty is rather a good thing, especially when it comes to passwords and keys. Some time ago I came across of an authentication mechanism in VBulletin, a forum web application, that transferred passwords as MD5 hashes over the network. Here is how an MD5 hash of a password looks like:

15fc3950b7c08c0b533f05a119795393 

At the first glance this hash value does look secure. However, since the hash algorithm is well defined as MD5(password), somebody could make their computer work day and night and compute billions and billions of MD5 hashes and store them in a database optimized for hash look-ups. Now anybody who has such database doesn't have to break a password hash, but simply can look it up in the database and learn the original password. Hard to believe? Google md5 hash online cracker and give that hash a try.

Lost in translation

Quite often developers use modulo arithmetic to convert a random number returned by functions, like rand(), to the required range of values. This works out fine when the destination range is small enough, but when it's comparable with the maximum random value, the balance breaks and numbers in the low range are more likely to appear in the result than numbers from the high range.

For example, the following code is intended to generate and use 10000 random values in the range from zero to 20 thousand.

for(int i = 0; i < 10000; i++)
   printf("%d\n", rand() % 20000);

The maximum value returned by rand() is 32767, so values above 12767 are less likely to be generated than those below. The graph below shows all 10000 values plotted in the order they were generated.

rand() mod 20K

It is quite obvious that somebody who would like to guess the numbers generated by this code would be more successful if they probe values in the lower part of the chart.

Bottom line

Avoid using rand() for security purposes, as it returns only a 15-bit random value. If you have to (e.g. for portability reasons), make sure to seed it properly. That is, srand() has to be called at the thread start. If you don't control threads, create your own thread when the application is initialized, seed the random generator when the thread is started and use this thread to fill a buffer with random bytes that can be retrieved by other threads.

On Windows, it is better to use a strong cryptographic random number generator, like the one provided by the CryptoAPI (CryptGenRandom). One thing to keep in mind, however, is that a good random number generator may be about 100 times slower than rand(), so populating a buffer with random bytes and then retreiving a few bytes at a time will help to improve performance.

Make sure to add random values to all your authentication hashes and tokens. Such random value, which is often called salt, will render existing precomputed hash databases useless, even though it is not a secret by itself and may be transmitted in clear text when required.

Comments:
Name:

Comment: