将密码从Drupal 7迁移到Django

时间:2012-03-26 17:20:36

标签: django drupal cryptography passwords sha

我正在将一个站点从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 snippetsGitHub gist。< / p>

4 个答案:

答案 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()