Я нахожусь в процессе перехода с Wordpress 2.8 на Django 1.8.Как я выяснил, Wordpress 2.8 (и, возможно, будущие версии) хранит пароль в криптографическом формате MD5 (библиотека phpass).Я попробовал расширение passlib для Django 1.8, но у меня оно не сработало.В итоге я написал собственный хеш с криптографическим алгоритмом 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',
)
hashers.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)),
])