Мой вариант использования следующий:
- У меня есть приложение CLI (+ щелчок + Python 3.8), который будет очень простым менеджером паролей (для личного использования).
Код: Выделить всё
SQLAlchemy
- При запуске я хочу запросить у пользователя главный пароль, чтобы он мог получить любую информацию из БД. Если у пользователя еще нет мастер-пароля, я попрошу его создать его. Я хочу, чтобы все данные были зашифрованы одним и тем же мастер-ключом.
Код: Выделить всё
import base64
from cryptography.fernet import Fernet, InvalidToken
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
def generate_key_derivation(salt, master_password):
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
key = base64.urlsafe_b64encode(kdf.derive(master_password.encode()))
return key
def encrypt(key, value_to_encrypt):
f = Fernet(key)
encrypted_key = f.encrypt(value_to_encrypt.encode())
return encrypted_key
def decrypt(key, encrypted_key):
f = Fernet(key)
try:
return f.decrypt(encrypted_key)
except InvalidToken:
return b''
В В этой схеме соль должна храниться в доступном месте, чтобы
получить тот же ключ из пароля в будущем.
Что в моей голове означает: хранить соль в БД и использовать ее каждый раз, когда пользователь пытается использовать приложение. Затем запустите мастер-пароль, введенный пользователем, с помощью функции получения ключа и проверьте, соответствует ли он… ключу? Но исходного ключа у меня нет, так как я не сохранил его в первый раз вместе с солью. И если бы я его сохранил, разве никто не смог бы просто использовать его для шифрования и дешифрования данных?
Какое обычное решение используется предотвратить вышеперечисленное?
Вот небольшой POC с использованием щелчка:
Код: Выделить всё
import os
import click
from models import MasterPasswordModel
@click.group(help="Simple CLI Password Manager for personal use")
@click.pass_context
def main(ctx):
# if the user hasn't stored any master password yet,
# create a new one
if MasterPasswordModel.is_empty():
# ask user for a new master password
master_password = click.prompt(
'Please enter your new master password: ',
hide_input=True
)
# generate the salt
salt = os.urandom(16)
# generate key_derivation
# this isn't stored because if it does anyone would be able
# to access any data
key = generate_key_derivation(salt, master_password)
# store the salt to the DB
MasterPasswordModel.create(salt)
# if the user stored a master password, check if it's valid and
# allow him to do other actions
else:
# ask user for existing master password
master_password = click.prompt(
'Please enter your new master password: ',
hide_input=True
)
# get existing master password salt from DB
salt = MasterPasswordModel.get_salt()
# generate key_derivation
key = generate_key_derivation(salt, master_password)
# At this point I don't know how to check whether the `key` is
# valid or not since I don't have anything to check it against.
# what am I missing?

ЛЕ: Как указано в одном из комментариев, это Похоже, у меня есть решение, но я все еще застреваю где-то в процессе. В этом ответе указано, что:
Если вы еще этого не делаете, я также настоятельно рекомендую не делать
с использованием предоставленного пользователем ключа напрямую, но вместо этого сначала передавая его
через намеренно медленную функцию получения ключа, такую как PBKDF2 ,
bcrypt или scrypt. Вам следует сделать это в первую очередь, прежде чем даже пытаться
проверить правильность ключа и немедленно отказаться от
оригинального, предоставленного пользователем ключ и используйте производный ключ для всего
(как для проверки, так и для фактического шифрования/дешифрования).
Итак , давайте для примера все по шагам:
1) У меня первый раз просят мастер-пароль. Его не существует в БД, поэтому, очевидно, мне нужно его создать и сохранить.
2) Наряду с вновь созданной солью мне нужно сохранить хэш предоставленный главный пароль (для примера я буду использовать SHA-256).
3) Теперь у меня есть запись, содержащая соль и хешированный главный пароль, поэтому я можете продолжить использование приложения. Теперь я хочу создать новую запись в БД, которая предположительно будет зашифрована с помощью моего ключа.
Вопрос в том... какой ключ? Если бы я применил то, что написано выше, мне пришлось бы использовать функциюgener_key_derivation(), используя соль и хешированный главный пароль из БД, и использовать его для шифрования/дешифрования. Но если я это сделаю, не сможет ли кто-нибудь просто взять hash_key, хранящийся в БД, и использовать тот же методgenerate_key_derivation, чтобы делать все, что захочет?
Итак, чего мне не хватает?
Подробнее здесь: https://stackoverflow.com/questions/619 ... d-use-case