我正在将一个站点从Drupal 7迁移到Django 1.4,包括当前用户。如何使用Drupal散列的密码?
根据this,Drupal 7使用SHA-512对密码进行哈希处理(它们以“$ S $”开头的字符串形式存储)。
Django 1.4现在包含许多用于存储密码的options,默认值为SHA-256,但我找不到SHA-512的选项。虽然this app似乎允许使用SHA2算法,但我不确定它是否与Django 1.4兼容(因为1.4具有灵活的密码哈希)。
最简单的方法是什么?
ETA:我已经构建了一个模拟Drupal算法的密码哈希,并使迁移变得简单。由于我已经接受了答案,我不会接受,但是对于任何想要将Drupal转移到Django迁移的人来说,代码存储在Django snippets和GitHub gist。< / p>
答案 0 :(得分:5)
我不太了解Drupal,但我认为密码是存储的哈希值。如果是这种情况,你将不得不复制密码(我的意思是,将它们保持不变),你将不得不改变Django散列密码的方式,使用与Drupal完全相同的方式,使用相同的安全盐。 / p>
我真的不知道该怎么做,但密码的逻辑包含在User对象中。例如。 User.set_password()
函数(描述为here)使用make_password函数。
我认为通过一些研究你会找到改变它的方法,但重要的是,记住功能必须是平等的!即:
对于允许的密码集中的每个x,drupal_hash(x)== django_hash(x)。
修改强>
深入了解django使用get_hasher函数获取has函数。现在在1.4版本中,有一种方法可以指定Django如何选择该功能。看看这个:https://docs.djangoproject.com/en/dev/topics/auth/#how-django-stores-passwords
最后,为了创建自己的功能,您可以查看MD5PasswordHasher上的功能。看起来很简单。您可以使用hashlib python library生成sha-512算法。
更改编码方法需要类似于:
的内容def encode(self, password, salt):
assert password
assert salt and '$' not in salt
hash = hashlib.sha512(salt + password).hexdigest()
return "%s$%s$%s" % (self.algorithm, salt, hash)
答案 1 :(得分:2)
谢谢大卫罗宾逊的代码。那是我的一天!但它似乎有一个缺陷:如果Drupal决定不使用'C'而不是'D'来进行迭代次数,那么它就会失败。我稍微修改了类定义:
class DrupalPasswordHasher(BasePasswordHasher):
algorithm = "S"
iter_code = 'C'
salt_length = 8
def encode(self, password, salt, iter_code=None):
"""The Drupal 7 method of encoding passwords"""
if iter_code == None:
iterations = 2 ** _ITOA64.index(self.iter_code)
else:
iterations = 2 ** _ITOA64.index(iter_code)
hash = hashlib.sha512(salt + password).digest()
for i in range(iterations):
hash = hashlib.sha512(hash + password).digest()
l = len(hash)
output = ''
i = 0
while i < l:
value = ord(hash[i])
i = i + 1
output += _ITOA64[value & 0x3f]
if i < l:
value |= ord(hash[i]) << 8
output += _ITOA64[(value >> 6) & 0x3f]
if i >= l:
break
i += 1
if i < l:
value |= ord(hash[i]) << 16
output += _ITOA64[(value >> 12) & 0x3f]
if i >= l:
break
i += 1
output += _ITOA64[(value >> 18) & 0x3f]
longhashed = "%s$%s%s%s" % (self.algorithm, iter_code,
salt, output)
return longhashed[:54]
def verify(self, password, encoded):
hash = encoded.split("$")[1]
iter_code = hash[0]
salt = hash[1:1 + self.salt_length]
return encoded == self.encode(password, salt, iter_code)
答案 2 :(得分:1)
您应该可以通过创建自己的BasePasswordHasher
子类并将其添加到PASSWORD_HASHERS
设置来实现此目的。
Python的hashlib实现了sha512。
page David linked to in the question解释了如何在哈希中编码迭代次数(Drupal 7的16385),但我不清楚如何获取盐。
编辑:在对@ santiago的答案的评论中,David说“盐是存储的Drupal字符串中第12个字符的第5个字符”。
答案 3 :(得分:0)
这是David对python 3的优秀答案的更新,因为hashlib不再接受字符串。此外,这包括对奇怪的“U $ S $ *”哈希的支持,这显然来自更新,我在我的drupal数据库中发现了一堆。
https://gist.github.com/skulegirl/bec420b5272b87d9e4dbd39e947062fc
作为奖励,这里是我用来导入用户数据的xml文件的代码。(我在用户表上执行查询后,通过sql导出创建了xml文件。)
import xml.etree.ElementTree as ET
from django.contrib.auth.models import User
tree = ET.parse('/PATH/TO/Users.xml')
root = tree.getroot()
for row in root:
user_dict = {}
for field in row:
user_dict[field.attrib['name']] = field.text
user = User.objects.create_user(user_dict['name'], user_dict['mail'])
if user_dict['pass'][0] == '$':
user_dict['pass'] = user_dict['pass'][1:]
user.password = user_dict['pass']
user.save()