Django – make_random_password method, is it truly random?

Question:

I am using the following method to create a random code for users as part of a booking process:

 User.objects.make_random_password()

When the users turn up at the venue, they will present the password.

Is it safe to assume that two people won’t end up with the same code?

Asked By: Imran Azad

||

Answers:

No, it’s not safe to assume that two people can’t have the same code. Random doesn’t mean unique. It may be unlikely and rare, depending on the length you specify and number of users you are dealing with. But you can’t rely on its uniqueness.

Answered By: TJD

It depends on now many users you have, and the password length you choose, and how you use User.objects.make_random_password() For the defaults, the chance is essentially zero, IMO;

This method is implemented using get_random_string(). From the django github repo:

def get_random_string(length=12,
                      allowed_chars='abcdefghijklmnopqrstuvwxyz'
                                    'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
    """
Returns a securely generated random string.

The default length of 12 with the a-z, A-Z, 0-9 character set returns
a 71-bit value. log_2((26+26+10)^12) =~ 71 bits
"""
    if not using_sysrandom:
        # This is ugly, and a hack, but it makes things better than
        # the alternative of predictability. This re-seeds the PRNG
        # using a value that is hard for an attacker to predict, every
        # time a random string is required. This may change the
        # properties of the chosen random sequence slightly, but this
        # is better than absolute predictability.
        random.seed(
            hashlib.sha256(
                "%s%s%s" % (
                    random.getstate(),
                    time.time(),
                    settings.SECRET_KEY)
                ).digest())
    return ''.join([random.choice(allowed_chars) for i in range(length)])

According to github, the current code uses a 12 character password from a string of 62 characters (lower- and uppercase letters and numbers) by default. This makes for 62**12 or 3226266762397899821056 (3.22e21) possible different passwords. This is much larger than the current world population (around 7e9).

The letters are picked from this list of characters by the random.choice() function. The question now becomes how likely it is that the repeated calling of random.choice() returns the same sequence twice?

As you can see from the implementation of get_random_string(), the code tries hard to avoid predictability. When not using the OS’s pseudo-random value generator (which on Linux and *BSD gathers real randomness from e.g. the times at which ethernet packets or keypresses arrive), it re-seeds the random module’s Mersenne Twister predictable PRNG at each call with a combination of the current random state, the current time and (presumably constant) secret key.

So for two identical passwords to be generated, both the state of the random generator (which is about 8 kiB in python) and the time at which they are generated (measured in seconds since the epoch, as per time.time()) have to be identical. If the system’s time is well-maintained and you are running one instance of the password generation program, the chance of that happening is essentially zero. If you start two or more instances of this password generating program at exactly the same time and with exactly the same seed for the PRNG, and combine their output, one would expect some passwords to appear more than once.

Answered By: Roland Smith
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.