1. Source code analysis
Version 1.4 released by Django contains some important security improvements. One of them is to use the PBKDF2 password encryption algorithm instead of SHA1. Another feature is that you can add your own password encryption method.
Django will use the first password encryption method you provide (at least one method in your file)
PASSWORD_HASHERS = [ '.PBKDF2PasswordHasher', '.PBKDF2SHA1PasswordHasher', '.Argon2PasswordHasher', '.BCryptSHA256PasswordHasher', '', ]
Let’s take a look at the PBKDF2PasswordHasher encryption method.
class BasePasswordHasher(object): """ Abstract base class for password hashers When creating your own hasher, you need to override algorithm, verify(), encode() and safe_summary(). PasswordHasher objects are immutable. """ algorithm = None library = None def _load_library(self): if is not None: if isinstance(, (tuple, list)): name, mod_path = else: name = mod_path = try: module = importlib.import_module(mod_path) except ImportError: raise ValueError("Couldn't load %s password algorithm " "library" % name) return module raise ValueError("Hasher '%s' doesn't specify a library attribute" % self.__class__) def salt(self): """ Generates a cryptographically secure nonce salt in ascii """ return get_random_string() def verify(self, password, encoded): """ Checks if the given password is correct """ raise NotImplementedError() def encode(self, password, salt): """ Creates an encoded database value The result is normally formatted as "algorithm$salt$hash" and must be fewer than 128 characters. """ raise NotImplementedError() def safe_summary(self, encoded): """ Returns a summary of safe values The result is a dictionary and will be used where the password field must be displayed to construct a safe representation of the password. """ raise NotImplementedError() class PBKDF2PasswordHasher(BasePasswordHasher): """ Secure password hashing using the PBKDF2 algorithm (recommended) Configured to use PBKDF2 + HMAC + SHA256. The result is a 64 byte binary string. Iterations may be changed safely but you must rename the algorithm if you change SHA256. """ algorithm = "pbkdf2_sha256" iterations = 36000 digest = hashlib.sha256 def encode(self, password, salt, iterations=None): assert password is not None assert salt and '$' not in salt if not iterations: iterations = hash = pbkdf2(password, salt, iterations, digest=) hash = base64.b64encode(hash).decode('ascii').strip() return "%s$%d$%s$%s" % (, iterations, salt, hash) def verify(self, password, encoded): algorithm, iterations, salt, hash = ('$', 3) assert algorithm == encoded_2 = (password, salt, int(iterations)) return constant_time_compare(encoded, encoded_2) def safe_summary(self, encoded): algorithm, iterations, salt, hash = ('$', 3) assert algorithm == return OrderedDict([ (_('algorithm'), algorithm), (_('iterations'), iterations), (_('salt'), mask_hash(salt)), (_('hash'), mask_hash(hash)), ]) def must_update(self, encoded): algorithm, iterations, salt, hash = ('$', 3) return int(iterations) != def harden_runtime(self, password, encoded): algorithm, iterations, salt, hash = ('$', 3) extra_iterations = - int(iterations) if extra_iterations > 0: (password, salt, extra_iterations)
As you can see, you must inherit from BasePasswordHasher and override the verification(), encode() and safe_summary() methods.
Django uses PBKDF 2 algorithm with 36,000 iterations to make it less easy to be easily compromised by brute force cracking. Passwords are stored in the following format:
algorithm$number of iterations$salt$password hash”
Example: pbkdf2_sha256$36000$Lx7auRCc8FUI$eG9lX66cKFTos9sEcihhiSCjI6uqbr9ZrO+Iq3H9xDU=
2. Custom password encryption method
1. Add a custom encryption algorithm to it:
PASSWORD_HASHERS = [ '.MyMD5PasswordHasher', '.PBKDF2PasswordHasher', '.PBKDF2SHA1PasswordHasher', '.Argon2PasswordHasher', '.BCryptSHA256PasswordHasher', '', ]
2. Let’s look at MyMD5PasswordHasher. This is my custom encryption method, which is the basic md5. Django’s MD5PasswordHasher is salted:
from import BasePasswordHasher,MD5PasswordHasher from import mask_hash import hashlib class MyMD5PasswordHasher(MD5PasswordHasher): algorithm = "mymd5" def encode(self, password, salt): assert password is not None hash = hashlib.md5(password).hexdigest().upper() return hash def verify(self, password, encoded): encoded_2 = (password, '') return () == encoded_2.upper() def safe_summary(self, encoded): return OrderedDict([ (_('algorithm'), algorithm), (_('salt'), ''), (_('hash'), mask_hash(hash)), ])
You can later see in the database that the password does use a custom encryption method.
3. Modify the authentication method
AUTHENTICATION_BACKENDS = ( '', #New '', '', )
4. Let’s look at the customized authentication method
: import hashlib from pro import models from import ModelBackend class MyBackend(ModelBackend): def authenticate(self, username=None, password=None): try: user = models.M_User.(username=username) print user except Exception: print 'no user' return None if hashlib.md5(password).hexdigest().upper() == : return user return None def get_user(self, user_id): try: return models.M_User.(id=user_id) except Exception: return None
Of course, after these modifications, the final security is much lower than that provided by django, but this is the requirement and must be met.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.