views:

110

answers:

1

I have an ASP.NET application that relies on the Random class to generate a pseudo-random string. It uses the following code (this is part of a larger piece of sample code provided by Google for apps SSO):

    public static class SamlUtility
{
    private static Random random = new Random();

    private static char[] charMapping =  { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p' };

    public static string CreateId()
    {
        byte[] bytes = new byte[20]; // 160 bits

        random.NextBytes(bytes);

        char[] chars = new char[40];

        for (int i = 0; i < bytes.Length; i++)
        {
            int left = (bytes[i] >> 4) & 0x0f;
            int right = bytes[i] & 0x0f;
            chars[i * 2] = charMapping[left];
            chars[i * 2 + 1] = charMapping[right];
        }

        return new string(chars);
    }
}

This normally works very well, but from time to time it starts generating a string of 'a'. From what I can tell from debugging, Random simply stops returning random numbers and instead fills bytes with the same value over and over. I've patched this by using a GUID instead, but I'm curious what happened in the original code. I'm assuming some form of entropy exhaustion, but I can't find any reference in the docs. Also, each time this has happened, performing iisreset restored the correct behavior.

Any suggestions regarding what was going wrong would be much appreciated.

+6  A: 

The Random class is not thread-safe.
If you generate random numbers on the same instance on multiple threads at once, its internal state will be corrupted and it will start returning zeroes.

You need to make the Random instance [ThreadStatic] to ensure that each instance is not shared by multiple threads.
Note that initializers for [ThreadStatic] fields will only run once, so you need to check whether it's null every time you use the field and initialize it if necessary.
It would also be a good idea to include both the thread ID and the current time in the seed to prevent seed collisions.

Note, by the way, that the Random class is not secure; consider using the RNGCryptoServiceProvider class

SLaks
Or you could protect the single Random instance with a lock.
Jim Mischel
@Jim: locks should be avoided where possible. (They're slow)
SLaks
Thanks, SLaks! I am only relying on the random number to provide uniqueness; is my solution to use GUID an acceptable solution or does it make more sense to revert to the use of Random and set it ThreadStatic?
Jacob
Somehow, I doubt that the time to obtain the lock (my testing shows that it's about 0.05 microseconds to get a lock when the resource isn't already locked) is going to matter much in the context of an ASP.NET web page. The "slow" lock is such a small amount of time compared with the time required to respond to the request, that it's of no consequence. If lock speed is really that important, use a SpinLock. "Locks are slow" is one of those bits of conventional wisdom that needs to be re-evaluated.
Jim Mischel
@Jacob: Random number is not going to guarantee uniqueness!
Jim Mischel
@Jim: Indeed. For most uses, locks are only slow *when they are contended*. And if the lock spends most of its time contended then *you have larger problems to solve*, namely that you have probably gated your entire program on access to a single resource.
Eric Lippert
@Jacob, if uniqueness is a requirement then a GUID isn't just acceptable, it's almost mandatory. That's why GUIDs were invented, to provide guaranteed unique numbers across all computers and all time.
Mark Ransom
@Jacob: Jim is right: *random numbers are not in the slightest bit unique*. The probability of a collision in random 32 bit numbers rises to over 50% after only 77000 numbers generated. See http://blogs.msdn.com/b/ericlippert/archive/2010/03/22/socks-birthdays-and-hash-collisions.aspx for details. **If you want global uniqueness then use a tool specifically designed for that job: the Globally Unique Identifier, aka GUID, is designed to solve your problem.* Hence its name.
Eric Lippert
Thanks for the comments regarding random numbers. The period of uniqueness is extremely short, but the point is well taken that GUIDs are the way to go. Thanks for the feedback!
Jacob