Code

Ticket #5600: django_rev_15346.patch

File django_rev_15346.patch, 5.4 KB (added by coh, 3 years ago)
  • docs/topics/auth.txt

     
    386386 
    387387That's hashtype, salt and hash, separated by the dollar-sign character. 
    388388 
    389 Hashtype is either ``sha1`` (default), ``md5`` or ``crypt`` -- the algorithm 
    390 used to perform a one-way hash of the password. Salt is a random string used 
    391 to salt the raw password to create the hash. Note that the ``crypt`` method is 
    392 only supported on platforms that have the standard Python ``crypt`` module 
    393 available. 
     389Hashtype is either ``sha1`` (default), ``sha256``, ``sha512``, ``md5`` or  
     390``crypt`` -- the algorithm used to perform a one-way hash of the password.  
     391Salt is a random string used to salt the raw password to create the hash.  
     392Note that the ``crypt`` method is only supported on platforms that have the  
     393standard Python ``crypt`` module available. Also note that the ``sha256`` and 
     394``sha512`` methods are only available under Python 2.5 or newer. 
    394395 
    395396For example:: 
    396397 
  • docs/ref/settings.txt

     
    117117authenticate a user. See the :doc:`authentication backends documentation 
    118118</ref/authbackends>` for details. 
    119119 
     120.. setting:: AUTH_HASH_ALGORITHM 
     121 
     122AUTH_HASH_ALGORITHM 
     123------------------- 
     124 
     125Default: ``'sha1'`` 
     126 
     127The hash algorithm that should be used by the authentication backend. 
     128Available options are ``sha1``, ``sha256``, ``sha512``, ``md5`` and ``crypt``. 
     129Please note that the two strongest ones, ``sha512`` and ``sha256``, are only  
     130available when running Python >= 2.5, since it uses the then-introduced  
     131``hashlib``. 
     132 
    120133.. setting:: AUTH_PROFILE_MODULE 
    121134 
    122135AUTH_PROFILE_MODULE 
  • django/conf/global_settings.py

     
    477477 
    478478AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',) 
    479479 
     480AUTH_HASH_ALGORITHM = 'sha1' 
     481 
    480482LOGIN_URL = '/accounts/login/' 
    481483 
    482484LOGOUT_URL = '/accounts/logout/' 
  • django/contrib/auth/models.py

     
    11import datetime 
    22import urllib 
    33 
     4from django.conf import settings 
    45from django.contrib import auth 
    56from django.contrib.auth.signals import user_logged_in 
    67from django.core.exceptions import ImproperlyConfigured 
     
    1415 
    1516UNUSABLE_PASSWORD = '!' # This will never be a valid hash 
    1617 
     18# we're using 160 bits for the salt length, and then 'only' use a 128 bit 
     19# chunk of that as the actual salt 
     20SALT_LENGTH = 28 # ceil(160/7) + 5 as safety margin 
     21SALT_HEX_LENGTH = 128 // 4 
     22 
     23 
    1724def get_hexdigest(algorithm, salt, raw_password): 
    1825    """ 
    1926    Returns a string of the hexdigest of the given plaintext password and salt 
     
    3138        return md5_constructor(salt + raw_password).hexdigest() 
    3239    elif algorithm == 'sha1': 
    3340        return sha_constructor(salt + raw_password).hexdigest() 
     41    elif algorithm in ('sha256', 'sha512'): 
     42        try: 
     43            import hashlib 
     44        except ImportError: 
     45            raise ValueError('"%s" password algorithm not supported in this environment' % algorithm) 
     46        thehash = None 
     47        if algorithm == 'sha256': 
     48            thehash = hashlib.sha256 
     49        else: 
     50            thehash = hashlib.sha512 
     51        return thehash(salt + raw_password).hexdigest() 
     52 
    3453    raise ValueError("Got unknown password algorithm type in password.") 
    3554 
    3655def check_password(raw_password, enc_password): 
     
    147166        "Generates a random password with the given length and given allowed_chars" 
    148167        # Note that default value of allowed_chars does not have "I" or letters 
    149168        # that look like it -- just to avoid confusion. 
    150         from random import choice 
    151         return ''.join([choice(allowed_chars) for i in range(length)]) 
     169        from random import sample 
     170        return ''.join(sample(allowed_chars, length)) 
    152171 
    153172 
    154173# A few helper functions for common logic between User and AnonymousUser. 
     
    251270        if raw_password is None: 
    252271            self.set_unusable_password() 
    253272        else: 
    254             import random 
    255             algo = 'sha1' 
    256             salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5] 
     273            import os 
     274            algo = settings.AUTH_HASH_ALGORITHM 
     275            salt = None 
     276            try: 
     277                # try to get some really strong salt first 
     278                salt = sha1_constructor( 
     279                    os.urandom(SALT_LENGTH))[:SALT_HEX_LENGTH] 
     280            except NotImplementedError: 
     281                # if that fails, use some weaker stuff 
     282                import random 
     283                salt = '' 
     284                salt_salt = '' 
     285                for i in xrange(SALT_LENGTH): 
     286                    salt += chr(random.randint(0, 255)) 
     287                    salt_salt += chr(random.randint(0, 255)) 
     288                # but let it work the extra mile 
     289                salt = get_hexdigest(algo, salt_salt, 
     290                    salt)[:SALT_HEX_LENGTH] 
    257291            hsh = get_hexdigest(algo, salt, raw_password) 
    258292            self.password = '%s$%s$%s' % (algo, salt, hsh) 
    259293