diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py index 2d45e71f1c9..5a637e24d23 100644 --- a/django/contrib/sessions/backends/base.py +++ b/django/contrib/sessions/backends/base.py @@ -1,7 +1,4 @@ import base64 -import hashlib -import os -import random import time from datetime import datetime, timedelta try: @@ -11,16 +8,11 @@ except ImportError: from django.conf import settings from django.core.exceptions import SuspiciousOperation -from django.utils.crypto import constant_time_compare, salted_hmac +from django.utils.crypto import constant_time_compare +from django.utils.crypto import get_random_string +from django.utils.crypto import salted_hmac from django.utils import timezone -# Use the system (hardware-based) random number generator if it exists. -if hasattr(random, 'SystemRandom'): - randrange = random.SystemRandom().randrange -else: - randrange = random.randrange -MAX_SESSION_KEY = 18446744073709551616L # 2 << 63 - class CreateError(Exception): """ Used internally as a consistent exception type to catch from save (see the @@ -138,17 +130,12 @@ class SessionBase(object): def _get_new_session_key(self): "Returns session key that isn't being used." - # The random module is seeded when this Apache child is created. - # Use settings.SECRET_KEY as added salt. - try: - pid = os.getpid() - except AttributeError: - # No getpid() in Jython, for example - pid = 1 + # Todo: move to 0-9a-z charset in 1.5 + hex_chars = '1234567890abcdef' + # session_key should not be case sensitive because some backends + # can store it on case insensitive file systems. while True: - session_key = hashlib.md5("%s%s%s%s" - % (randrange(0, MAX_SESSION_KEY), pid, time.time(), - settings.SECRET_KEY)).hexdigest() + session_key = get_random_string(32, hex_chars) if not self.exists(session_key): break return session_key diff --git a/django/core/management/commands/startproject.py b/django/core/management/commands/startproject.py index c849ab69c37..a1a9b33a439 100644 --- a/django/core/management/commands/startproject.py +++ b/django/core/management/commands/startproject.py @@ -1,7 +1,6 @@ -from random import choice - from django.core.management.base import CommandError from django.core.management.templates import TemplateCommand +from django.utils.crypto import get_random_string from django.utils.importlib import import_module @@ -27,6 +26,6 @@ class Command(TemplateCommand): # Create a random SECRET_KEY hash to put it in the main settings. chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)' - options['secret_key'] = ''.join([choice(chars) for i in range(50)]) + options['secret_key'] = get_random_string(50, chars) super(Command, self).handle('project', project_name, target, **options) diff --git a/django/utils/crypto.py b/django/utils/crypto.py index 4fe11f428c1..a54c455e1bb 100644 --- a/django/utils/crypto.py +++ b/django/utils/crypto.py @@ -7,6 +7,13 @@ import struct import hashlib import binascii import operator + +import random +try: + random = random.SystemRandom() +except NotImplementedError: + pass + from django.conf import settings @@ -43,13 +50,8 @@ def get_random_string(length=12, Returns a random string of length characters from the set of a-z, A-Z, 0-9. The default length of 12 with the a-z, A-Z, 0-9 character set returns - a 71-bit salt. log_2((26+26+10)^12) =~ 71 bits + a 71-bit value. log_2((26+26+10)^12) =~ 71 bits """ - import random - try: - random = random.SystemRandom() - except NotImplementedError: - pass return ''.join([random.choice(allowed_chars) for i in range(length)])