Django 1.4 密码存储机制解析与自定义实现指南
本文深入分析Django 1.4版本引入的PBKDF2密码加密算法,详细解读其通过36,000次迭代和盐值提升安全性的机制。同时提供完整教程,指导开发者如何在settings.py中配置自定义密码加密方法,并修改认证后端以兼容非标准哈希格式,满足特定业务需求。
Django 1.4 密码存储机制解析
一、源码分析
Django在1.4版本中强化了安全机制,主要改进包括采用PBKDF2密码加密算法取代原有的SHA1,并支持用户自定义密码加密策略。
系统默认启用配置列表中的第一个密码加密方法,因此必须在settings.py文件中至少定义一种有效的加密方式。
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
]
首先分析Django内置的PBKDF2PasswordHasher实现逻辑。
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 self.library is not None:
if isinstance(self.library, (tuple, list)):
name, mod_path = self.library
else:
name = mod_path = self.library
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 = self.iterations
hash = pbkdf2(password, salt, iterations, digest=self.digest)
hash = base64.b64encode(hash).decode('ascii').strip()
return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
def verify(self, password, encoded):
algorithm, iterations, salt, hash = encoded.split('$', 3)
assert algorithm == self.algorithm
encoded_2 = self.encode(password, salt, int(iterations))
return constant_time_compare(encoded, encoded_2)
def safe_summary(self, encoded):
algorithm, iterations, salt, hash = encoded.split('$', 3)
assert algorithm == self.algorithm
return OrderedDict([
(_('algorithm'), algorithm),
(_('iterations'), iterations),
(_('salt'), mask_hash(salt)),
(_('hash'), mask_hash(hash)),
])
def must_update(self, encoded):
algorithm, iterations, salt, hash = encoded.split('$', 3)
return int(iterations) != self.iterations
def harden_runtime(self, password, encoded):
algorithm, iterations, salt, hash = encoded.split('$', 3)
extra_iterations = self.iterations - int(iterations)
if extra_iterations > 0:
self.encode(password, salt, extra_iterations)
如代码所示,自定义哈希器需继承BasePasswordHasher基类,并实现verify()、encode()及safe_summary()三个核心方法。
Django通过PBKDF2算法结合36,000次迭代计算,显著增加暴力破解难度。密码在数据库中以特定格式存储。
存储格式为:algorithm$number of iterations$salt$password hash。
例如:pbkdf2_sha256$36000$Lx7auRCc8FUI$eG9lX66cKFTos9sEcihhiSCjI6uqbr9ZrO+Iq3H9xDU=
自定义密码加密与认证实现
二、自定义密码加密方法
1、在settings.py中注册自定义加密算法。
PASSWORD_HASHERS = [
'myproject.hashers.MyMD5PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
]
2、查看MyMD5PasswordHasher实现,该自定义方式仅使用基础MD5,而Django内置的MD5PasswordHasher包含加盐处理。
from django.contrib.auth.hashers import BasePasswordHasher,MD5PasswordHasher
from django.contrib.auth.hashers 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 = self.encode(password, '')
return encoded.upper() == encoded_2.upper()
def safe_summary(self, encoded):
return OrderedDict([
(_('algorithm'), algorithm),
(_('salt'), ''),
(_('hash'), mask_hash(hash)),
])
配置完成后,数据库中存储的密码将体现自定义加密逻辑。
3、修改认证后端配置。
AUTHENTICATION_BACKENDS = (
'framework.mybackend.MyBackend', #新加
'django.contrib.auth.backends.ModelBackend',
'guardian.backends.ObjectPermissionBackend',
)
4、实现自定义认证后端逻辑。
framework.mybackend.py:
import hashlib
from pro import models
from django.contrib.auth.backends import ModelBackend
class MyBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = models.M_User.objects.get(username=username)
print user
except Exception:
print 'no user'
return None
if hashlib.md5(password).hexdigest().upper() == user.password:
return user
return None
def get_user(self, user_id):
try:
return models.M_User.objects.get(id=user_id)
except Exception:
return None
尽管上述修改导致安全性低于Django默认方案,但需根据具体业务需求进行适配。
本文内容至此结束,希望能为读者提供有益参考,欢迎继续支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









