diff options
| author | Paul McMillan <Paul@McMillan.ws> | 2012-02-23 22:51:14 +0000 |
|---|---|---|
| committer | Paul McMillan <Paul@McMillan.ws> | 2012-02-23 22:51:14 +0000 |
| commit | 1525874238fd705ec17a066291935a9316bd3044 (patch) | |
| tree | 9ec5c31693492981606a0699c2c987e16c8b50e9 /django | |
| parent | 239e41f7c551308cea64561eed43ef2e81b60370 (diff) | |
Improved get_random_string().
Improved the behavior of get_random_string to re-seed itself each time it is called
if the system does not have a secure random number generator. This will change the
properties of the random string produced, but will be unpredictable to an attacker.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17581 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
| -rw-r--r-- | django/utils/crypto.py | 24 |
1 files changed, 22 insertions, 2 deletions
diff --git a/django/utils/crypto.py b/django/utils/crypto.py index a54c455e1b..44b7faf492 100644 --- a/django/utils/crypto.py +++ b/django/utils/crypto.py @@ -7,12 +7,18 @@ import struct import hashlib import binascii import operator +import time +# Use the system PRNG if possible import random try: random = random.SystemRandom() + using_sysrandom = True except NotImplementedError: - pass + import warnings + warnings.warn('A secure pseudo-random number generator is not available ' + 'on your system. Falling back to Mersenne Twister.') + using_sysrandom = False from django.conf import settings @@ -47,11 +53,25 @@ def get_random_string(length=12, allowed_chars='abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'): """ - Returns a random string of length characters from the set of a-z, A-Z, 0-9. + 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)]) |
