我正在从 Wordpress 2.8 迁移到 Django 1.8。我发现 Wordpress 2.8(可能还有未来的版本)以 MD5 加密格式(phpass 库)存储密码。我尝试了 Django 1.8 的 passlib 扩展,但它对我不起作用。所以我最终用 MD5 加密算法编写了自定义哈希。
注意:在迁移期间将“md5_crypt”添加到密码哈希(user_pass 字段)
我将 MD5CryptPasswordHasher 添加到列表顶部以使其成为默认值(为了不混淆不同的哈希算法,如果我将再次迁移到另一个平台怎么办?)但它可以添加到列表底部如果只想为现有用户添加对算法的支持但强制新用户迁移到 PBKDF2PasswordHasher 哈希或其他。
settings.py
PASSWORD_HASHERS = (
'your_project_name.hashers.MD5CryptPasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher',
'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
)
hasers.py
import math
import hashlib
from django.contrib.auth.hashers import BasePasswordHasher
from django.utils.crypto import get_random_string
from django.contrib.auth.hashers import mask_hash
from collections import OrderedDict
from django.utils.translation import ugettext, ugettext_lazy as _
itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
def encode64(inp, count):
outp = ''
cur = 0
while cur < count:
value = inp[cur]
cur += 1
outp += itoa64[value & 0x3f]
if cur < count:
value |= (inp[cur] << 8)
outp += itoa64[(value >> 6) & 0x3f]
if cur >= count:
break
cur += 1
if cur < count:
value |= (inp[cur] << 16)
outp += itoa64[(value >> 12) & 0x3f]
if cur >= count:
break
cur += 1
outp += itoa64[(value >> 18) & 0x3f]
return outp.encode()
def crypt_private(pw, algorithm, code, salt, iterations):
header = "%s$%s$%s%s" % (algorithm, code, itoa64[int(math.log(iterations, 2))], salt)
pw = pw.encode()
salt = salt.encode()
hx = hashlib.md5(salt + pw).digest()
while iterations:
hx = hashlib.md5(hx + pw).digest()
iterations -= 1
return header + encode64(hx, 16).decode()
def get_md5_crypto_hash_params(encoded):
algorithm, code, rest = encoded.split('$', 2)
count_log2 = itoa64.find(rest[0])
iterations = 1 << count_log2
salt = rest[1:9]
return (algorithm, salt, iterations)
class MD5CryptPasswordHasher(BasePasswordHasher):
"""
The Salted MD5 Crypt password hashing algorithm that is used by Wordpress 2.8
WARNING!
The algorithm is not robust enough to handle any kind of MD5 crypt variations
It was stripped and refactored based on passlib implementations especially for Wordpress 2.8 format
"""
algorithm = "md5_crypt"
iterations = 8192
code = "P" # Modular Crypt prefix for phpass
salt_len = 8
def salt(self):
return get_random_string(salt_len)
def encode(self, password, salt):
assert password is not None
assert salt != ''
return crypt_private(password, self.algorithm, self.code, salt, self.iterations)
pass
def verify(self, password, encoded):
algorithm, salt, iterations = get_md5_crypto_hash_params(encoded)
assert algorithm == self.algorithm
return crypt_private(password, algorithm, self.code, salt, iterations) == encoded
def safe_summary(self, encoded):
algorithm, code, rest = encoded.split('$', 2)
salt = rest[1:9]
hash = rest[9:]
assert algorithm == self.algorithm
return OrderedDict([
(_('algorithm'), algorithm),
(_('salt'), mask_hash(salt, show=2)),
(_('hash'), mask_hash(hash)),
])