在 Python 中将带有字符串键和字节值的字典保存到文件

Saving a dictionary with string keys and bytes values to file in Python

提问人:Flash_Steel 提问时间:2/13/2023 更新时间:2/13/2023 访问量:118

问:

我正在编写一个 python 程序,它应该能够在 Windows、iOS 和 Android 上运行。

为了处理登录,我有一个 目前,我手动将任何字符串转换为二进制,并将它们全部连接成一个二进制字符串,我在从文件加载凭据时解析。一旦它采用可读的二进制格式文件,I/O 就非常微不足道了dict{key_string: hashed_password_bytes}

代码很长(如下)。有谁知道一个包可以将字典写入文件,而不是使用明文,它可以做同样的工作?

我尝试过的软件包,但找不到合适的使用方法

  • 泡菜。由于代码注入风险 https://docs.python.org/3/library/pickle.html 而丢弃
  • YAML、JSON 等,因为我不想使用明文解决方案来存储凭据。
  • Struct 要求您在打开之前知道变量和字符串变量的长度。
  • Netstruct 允许可变长度的字符串,但是你,我不知道如何将字典解压缩到其中,或者所需的格式字符串以返回未知数量的字符串和字节变量的混合。
  • Pyvault 可以消除此任务的痛苦,但不幸的是它需要 sqlcipher,我不想在运行该程序的每台设备上安装它。

当前解决方案(在经过大量调试后,打印调用显然将被删除):

import scrypt
import os

MAX_TIME = 0.5  # Set here for ease of changing
FILE_001_DAT_LOCATION = "Source/001.dat"
DATA_LENGTH = 64  # Data length for hashing password
ENCODING_STRING = 'utf-8'  # Used in all string encode and decode
PACKING_SIZE_STRING_LENGTH = 4  # The number of bytes used by the size indicator for the proceeding username/password
SIZE_STRING_FORMAT = '{:0>4}'  # Used to format the size integer for packing dictionaries


# Turns {string: bytes} into bytes for writing to file
# Format is username1size, username1, password1size, password1, username2size...
def pack_credentials(credentials: dict):
    bytes_out = b""

    print("pack_credentials() called. Starting loop")

    # Loop over every key and add keys
    for key in credentials.keys():
        # Pack username
        print(f"Encoding username: {key}")
        encoded_username = key.encode(ENCODING_STRING)
        username_size = len(encoded_username)
        print(f"Encoded username has size: {username_size}")
        username_size_string = SIZE_STRING_FORMAT.format(username_size)
        bytes_out += username_size_string.encode(ENCODING_STRING) + encoded_username

        # Pack hashed password
        print(f"Encoding password: {str(credentials[key])}")
        password_size = len(credentials[key])
        print(f"Hashed password has size: {password_size}")
        password_size_string = SIZE_STRING_FORMAT.format(password_size)
        bytes_out += password_size_string.encode(ENCODING_STRING) + credentials[key]

    print("Packing complete. Returning bytes.")
    return bytes_out


# Hash a raw password and return - for adding new credentials to the dictionary
def hash_password(password):
    return scrypt.encrypt(os.urandom(DATA_LENGTH), password, maxtime=MAX_TIME)


# Turns {string: bytes} into bytes for writing to file
# Format is username1size, username1, password1size, password1, username2size...
def unpack_credentials(bytes_in: bytes):
    credentials_out = {}  # Defined here to add to in loop
    slice_start = 0  # Starting index for next slice from bytes_in
    slice_end = slice_start + PACKING_SIZE_STRING_LENGTH  # Ending index for next slice from bytes_in
    bytes_in_length = len(bytes_in)

    print("unpack_credentials() called. Starting loop")

    # Loop over every key and add keys
    while slice_end < bytes_in_length:
        # Unpack username
        print(f"Unpacking username size")
        username_length_bytes = bytes_in[slice_start:slice_end]
        print(f"Username size: {username_length_bytes}")
        print(f"Unpacking username")
        slice_start = slice_end
        slice_end = slice_end + int(username_length_bytes.decode(ENCODING_STRING))
        username_bytes = bytes_in[slice_start:slice_end]
        print(f"Username: {username_bytes.decode(ENCODING_STRING)}")

        # Pack hashed password
        print(f"Unpacking password size")
        slice_start = slice_end
        slice_end = slice_end + PACKING_SIZE_STRING_LENGTH
        password_length_bytes = bytes_in[slice_start:slice_end]
        print(f"Password size: {password_length_bytes.decode(ENCODING_STRING)}")
        slice_start = slice_end
        slice_end = slice_end + int(password_length_bytes.decode(ENCODING_STRING))
        print(f"Unpacking password")
        password_bytes = bytes_in[slice_start:slice_end]

        #Prepare for next iteration
        slice_start = slice_end
        slice_end = slice_end + PACKING_SIZE_STRING_LENGTH


        #Populate dict
        credentials_out[username_bytes.decode(ENCODING_STRING)] = password_bytes

    print("Unpacking complete. Returning dict.")
    return credentials_out


if __name__ == '__main__':
    # appGUI = MyApp()
    # appGUI.run()

    original_credentials = {"User 1 boiiiiiii": hash_password("password1wEDFRAwerwq rq"),
                   "Sonic the hedgehog": hash_password("password2qwergasdfghwerterwqgdfsg"),
                   "Misty water colour memories": hash_password("password3qdaserfgwqeryhger5wtywrthsfdghsd"),
                   "Niko the big black dog": hash_password("password4eadgrasewrdgerwqttgeqrwtgdfgewrtyertertgeertet")}

    credentials_binary = pack_credentials(original_credentials)
    new_credentials = unpack_credentials(credentials_binary)
    print(
        f"Do password hashes match? {original_credentials['User 1 boiiiiiii'] == new_credentials['User 1 boiiiiiii']}")
python-3.x io 凭据

评论

1赞 Kelly Bundy 2/13/2023
你到底对“明文”有什么看法,你自己的格式不是明文吗?
0赞 buran 2/13/2023
请查看帮助中心
2赞 buran 2/13/2023
再看一遍问题,这看起来像是XY问题。你真的问如何在数据库/文件中存储哈希(我希望也是加盐的)密码吗?
1赞 Maurice Meyer 2/13/2023
json + zip?
1赞 Kelly Bundy 2/13/2023
我不认为你用你的这种格式增加了安全性,你只是让自己不方便。

答: 暂无答案