Кодирование стратегии обеспечения уязвимых данных

Веб-приложение содержит уязвимые данные пользователей. Ни оператор веб-приложения, ни поставщик услуг хостинга не должны мочь видеть эти данные. Поэтому я хотел хранить эти данные в DB, зашифрованном с паролем входа пользователей.

dataInDB = encrypt (rawData, user password) 

С этой стратегией однако не возможно реализовать обычный вариант использования для восстановления пароля: С тех пор обычно только значение хэш-функции пароля хранится веб-приложением, приложение не может отправить старый, забытый пароль пользователю. И с присвоением нового, совпадающего пароля зашифрованные данные в DB больше не читаемы.

Есть ли какое-либо другое решение?

10
задан user151019 7 May 2012 в 21:53
поделиться

1 ответ

Возможное решение (я не несу ответственности за любое уничтожение):

При шифровании конфиденциального data, не используйте пароль пользователя в качестве ключа. Напротив, выводят ключ из пароля пользователя (предпочтительно с использованием стандартного алгоритма, такого как PBKDF2). На всякий случай, если пользователь забудет свой пароль, вы можете сохранить копию этого производного ключа (зашифрованного с использованием другого ключа, полученного из ответа пользователя ). Если пользователь забудет свой пароль, он сможет ответить на секретный вопрос. Только правильный ответ расшифрует исходный пароль ключ (а не исходный пароль). Это дает вам возможность повторно зашифровать конфиденциальную информацию.

Я продемонстрирую использование псевдокода (в стиле Python), но сначала давайте посмотрим на возможную таблицу для пользователей.Пока не зацикливайтесь на столбцах, они скоро прояснятся ...

CREATE TABLE USERS
(
    user_name               VARCHAR,

    -- ... lots of other, useful columns ...

    password_key_iterations NUMBER,
    password_key_salt       BINARY,
    password_key_iv         BINARY,
    encrypted_password_key  BINARY,
    question                VARCHAR,
    answer_key_iterations   NUMBER,
    answer_key_salt         BINARY
)

Когда приходит время регистрировать пользователя, он должен задать вопрос и ответ:

def register_user(user_name, password, question, answer):
    user = User()

    # The question is simply stored for later use
    user.question = question

    # The password secret key is derived from the user's password
    user.password_key_iterations = generate_random_number(from=1000, to=2000)
    user.password_key_salt = generate_random_salt()
    password_key = derive_key(password, iterations=user.password_key_iterations, salt=user.password_key_salt)

    # The answer secret key is derived from the answer to the user's security question
    user.answer_key_iterations = generate_random_number(from=1000, to=2000)
    user.answer_key_salt = generate_random_salt()
    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt)

    # The password secret key is encrypted using the key derived from the answer
    user.password_key_iv = generate_random_iv()
    user.encrypted_password_key = encrypt(password_key, key=answer_key, iv=user.password_key_iv)

    database.insert_user(user)

Если пользователь забыл свой пароль, системе все равно придется попросить пользователя ответить на секретный вопрос. Их пароль не может быть восстановлен, но ключ , полученный из пароля, может быть восстановлен. Это позволяет системе повторно зашифровать конфиденциальную информацию с помощью нового пароля:

def reset_password(user_name, answer, new_password):
    user = database.rerieve_user(user_name)

    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt)

    # The answer key decrypts the old password key
    old_password_key = decrypt(user.encrypted_password_key, key=answer_key, iv=user.password_key_iv)

    # TODO: Decrypt sensitive data using the old password key

    new_password_key = derive_key(new_password, iterations=user.password_key_iterations, salt=user.password_key_salt)

    # TODO: Re-encrypt sensitive data using the new password key

    user.encrypted_password_key = encrypt(new_password_key, key=user.answer_key, iv=user.password_key_iv)

    database.update_user(user)

Конечно, есть некоторые общие криптографические принципы, явно не выделенные здесь (режимы шифрования и т. Д.), Которые являются Ответственность исполнителя за ознакомление с ними.

Надеюсь, это немного поможет! :)

Обновление любезно предоставлено комментарием Eadwacer

Как прокомментировал Eadwacer:

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

Вот модифицированная версия моего решения с учетом его прекрасного совета:

CREATE TABLE USERS
(
    user_name                      VARCHAR,

    -- ... lots of other, useful columns ...

    password_key_iterations        NUMBER,
    password_key_salt              BINARY,
    password_encrypted_data_key    BINARY,
    password_encrypted_data_key_iv BINARY,
    question                       VARCHAR,
    answer_key_iterations          NUMBER,
    answer_key_salt                BINARY,
    answer_encrypted_data_key      BINARY,
    answer_encrypted_data_key_iv   BINARY,
)

Затем вы должны зарегистрировать пользователя следующим образом:

def register_user(user_name, password, question, answer):
    user = User()

    # The question is simply stored for later use
    user.question = question

    # The randomly-generated data key will ultimately encrypt our sensitive data
    data_key = generate_random_key()

    # The password key is derived from the password
    user.password_key_iterations = generate_random_number(from=1000, to=2000)
    user.password_key_salt = generate_random_salt()
    password_key = derive_key(password, iterations=user.password_key_iterations, salt=user.password_key_salt)

    # The answer key is derived from the answer
    user.answer_key_iterations = generate_random_number(from=1000, to=2000)
    user.answer_key_salt = generate_random_salt()
    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt)

    # The data key is encrypted using the password key
    user.password_encrypted_data_key_iv = generate_random_iv()
    user.password_encrypted_data_key = encrypt(data_key, key=password_key, iv=user.password_encrypted_data_key_iv)

    # The data key is encrypted using the answer key
    user.answer_encrypted_data_key_iv = generate_random_iv()
    user.answer_encrypted_data_key = encrypt(data_key, key=answer_key, iv=user.answer_encrypted_data_key_iv)

    database.insert_user(user)

Теперь сброс пароля пользователя выглядит так:

def reset_password(user_name, answer, new_password):
    user = database.rerieve_user(user_name)

    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt)

    # The answer key decrypts the data key
    data_key = decrypt(user.answer_encrypted_data_key, key=answer_key, iv=user.answer_encrypted_data_key_iv)

    # Instead of re-encrypting all the sensitive data, we simply re-encrypt the password key
    new_password_key = derive_key(new_password, iterations=user.password_key_iterations, salt=user.password_key_salt)

    user.password_encrypted_data_key = encrypt(data_key, key=new_password_key, iv=user.password_encrypted_data_key_iv)

    database.update_user(user)

Надеюсь, моя голова все еще нормально функционирует сегодня вечером ...

7
ответ дан 4 December 2019 в 02:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: