Перенос паролей из Drupal 7 в Django

Я переношу сайт с Drupal 7 на Django 1.4, включая текущих пользователей. Как я могу работать с паролями, которые были хэшированы Drupal?

Согласно this, Drupal 7 использует хэш-пароли с использованием SHA-512 (они хранятся в форме строки, начинающейся с "$S $" ).

Django 1.4 теперь содержит ряд параметров для хранения паролей со значением по умолчанию SHA-256, но я не могу найти вариант для SHA-512. В то время как это приложение позволяет использовать алгоритмы SHA2, я не уверен, что он совместим с Django 1.4 (поскольку у 1.4 есть гибкий хешер пароля).

Каков самый простой способ сделать это?

ETA: Я создал хакер пароля, который имитирует алгоритм Drupal и упрощает миграцию. Поскольку я уже принял ответ, я не буду признателен, но для тех, кто хочет в будущем переносить Drupal на Django, код хранится на Django и как GitHub gist.

Ответ 1

Я не знаю Drupal очень хорошо, но я полагаю, что пароли хранятся хэшированными. Если это произойдет, вам придется скопировать пароли (я имею в виду, скопируйте их без изменений), и вам придется изменить способ, которым Django использует свои пароли, используя точно такой же способ Drupal, с той же Солей безопасности.

Я действительно не знаю, как это сделать, но логика паролей содержится в объекте User. Например. функция User.set_password() (описанная здесь здесь) использует функцию make_password.

Я думаю, что с небольшим исследованием вы найдете способ изменить его, но важно помнить, что функции должны быть равны! то есть:

drupal_hash (x) == django_hash (x) для каждого x в разрешенных наборах паролей.

EDIT:

Взяв более глубокий взгляд django, получим функцию с функцией get_hasher. Теперь в версии 1.4 есть способ указать, как Django будет выбирать эту функцию. Взгляните на это: https://docs.djangoproject.com/en/dev/topics/auth/#how-django-stores-passwords

Наконец, чтобы создать свою собственную функцию, вы можете посмотреть, как это делается на MD5PasswordHasher. Это кажется очень простым. Вы можете использовать библиотеку python hashlib для генерации алгоритмов 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)

Ответ 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)

Ответ 3

Вы должны реализовать это, создав свой собственный подкласс BasePasswordHasher и добавив его в настройку PASSWORD_HASHERS.

Python hashlib реализует sha512.

страница Дэвид связан в вопросе объясняет, как число итераций (16385 для Drupal 7) кодируется в хэш, но это не понятно мне, как получить соль.

Изменить: В комментарии к ответу @santiago Дэвид говорит, что "соль является пятым символом через 12-е место в сохраненной строке Drupal".

Ответ 4

Здесь обновление для David отлично подходит для python 3, так как hashlib больше не принимает строки. Кроме того, это включает поддержку нечетных хэшей "U $S $*", которые, по-видимому, связаны с обновлением, и я нашел их в своей базе данных drupal.

https://gist.github.com/skulegirl/bec420b5272b87d9e4dbd39e947062fc

И в качестве бонуса здесь используется код, который я использовал для импорта моего xml файла пользовательских данных. (Я просто создал файл xml через экспорт sql после выполнения запроса в таблице users.)

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