您如何安全地将敏感数据从内存移动到本地磁盘?

How do you securely move sensitive data from memory to a local disk?

这个问题的目的

创建此问题是为了共享信息,因为各种模块的文档仅限于此主题。这个问题可能会成为一个社区维基。

用例

如何安全地将敏感数据从内存移动到磁盘?

更广泛的用例是如何通过将敏感数据写入本地磁盘上的临时文件来做到这一点而不暴露敏感数据。

什么是敏感数据?

敏感数据是必须受到保护且外部各方无法访问的信息,除非特别授予查看数据的权限。此类数据可以包括但不限于 PII(个人身份信息)、PHI(受保护的健康信息)或 GDPR 敏感数据。

在某些情况下,敏感数据可能是在出于法律原因(例如监视令)或秘密原因(例如监视潜在对手)对计算机系统进行调查期间生成的。

有多种方法可以实现这一点,我将提供几个使用明文数据或加密数据和带密码保护的加密压缩文件的示例。

pyzipper


这是使用 pyzipper

完成此用例的一种方法
import pyzipper

# generate data
secret_data = b'xyz' * 10

# password for the zip file
secret_password = b'super secret password'

# Create encrypted password protected ZIP file on disk
with pyzipper.AESZipFile('password_protected.zip',
                         'w',
                         compression=pyzipper.ZIP_LZMA,
                         encryption=pyzipper.WZ_AES) as zf:
    zf.setpassword(secret_password)
    zf.writestr('secret_data.txt', secret_data)


# Read encrypted password protected ZIP file from disk
with pyzipper.AESZipFile('password_protected.zip', 'r') as zf:
    zf.setpassword(secret_password)
    my_secrets = zf.read('secret_data.txt')
    print(my_secrets)
    # output
    xyzxyzxyzxyzxyzxyzxyzxyzxyzxyz

当我审查 pyzipper 的代码时,不清楚 secret data 在读写过程中是否受到保护。为了额外的安全性,我决定使用 cryptography 包对 secret data 进行加密。该软件包于 2014 年首次发布,并由数百名贡献者持续维护。

import os
import base64
import pyzipper
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

# password for the cryptographic process
password = b"password"

# random salt for the cryptographic process
salt = os.urandom(16)

# key derivation function (KDF) for the cryptographic process
kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    iterations=390000,
)

# create the encryption key for the cryptographic process
encryption_key = base64.urlsafe_b64encode(kdf.derive(password))

# https://cryptography.io/en/latest/fernet/
f = Fernet(encryption_key)

# generate data
secret_data = b'xyz' * 10

encrypted_data = f.encrypt(secret_data)

# password for the zip file
secret_password = b'super secret password'

# Create encrypted password protected ZIP file on disk
with pyzipper.AESZipFile('password_protected.zip',
                         'w',
                         compression=pyzipper.ZIP_LZMA,
                         encryption=pyzipper.WZ_AES) as zf:
    zf.setpassword(secret_password)
    zf.writestr('encryption_key.txt', encryption_key)
    zf.writestr('encrypted.txt', encrypted_data)


# Read encrypted password protected ZIP file from disk
# and decrypt data
with pyzipper.PyZipFile('password_protected.zip', 'r') as zf:
    zf.setpassword(secret_password)
    my_secrets = zf.read('encrypted.txt')
    my_key = zf.read('encryption_key.txt')
    f2 = Fernet(my_key)
    decrypt_message = f2.decrypt(my_secrets)
    print(decrypt_message)
    # output
    b'xyzxyzxyzxyzxyzxyzxyzxyzxyzxyz'

为了增加一层安全性,可以使用 in-memory file system。在这种情况下,secret data 被加密,然后写入 in-memory 文件,然后写入加密密码保护的 ZIP 文件。

import fs
import os
import base64
import pyzipper
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

# create in-memory file system
mem_fs = fs.open_fs('mem://')
mem_fs.makedir('hidden_dir')

# password for the cryptographic process
password = b"password"

# random salt for the cryptographic process
salt = os.urandom(16)

# key derivation function (KDF) for the cryptographic process
kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    iterations=390000,
)

# create the encryption key for the cryptographic process
encryption_key = base64.urlsafe_b64encode(kdf.derive(password))

# https://cryptography.io/en/latest/fernet/
f = Fernet(encryption_key)

# generate data
secret_data = b'xyz' * 10

encrypted_data = f.encrypt(secret_data)

# password for the zip file
secret_password = b'super secret password'


# write encryption key to an in-memory file
with mem_fs.open('hidden_dir/encryption_key.txt', 'wb') as in_file_in_memory:
    in_file_in_memory.write(encryption_key)
    in_file_in_memory.close()

# write encrypted data to an in-memory file
with mem_fs.open('hidden_dir/encrypted.txt', 'wb') as in_file_in_memory:
    in_file_in_memory.write(encrypted_data)
    in_file_in_memory.close()


# Create encrypted password protected ZIP file on disk
with pyzipper.AESZipFile('password_protected.zip',
                         'w',
                         compression=pyzipper.ZIP_LZMA,
                         encryption=pyzipper.WZ_AES) as zf:
    zf.setpassword(secret_password)
    zf.writestr('encryption_key.txt', mem_fs.readbytes('hidden_dir/encryption_key.txt'))
    zf.writestr('encrypted.txt', mem_fs.readbytes('hidden_dir/encrypted.txt'))


# Read encrypted password protected ZIP file from disk
# and decrypt data
with pyzipper.PyZipFile('password_protected.zip', 'r') as zf:
    zf.setpassword(secret_password)
    my_secrets = zf.read('encrypted.txt')
    my_key = zf.read('encryption_key.txt')
    f2 = Fernet(my_key)
    decrypt_message = f2.decrypt(my_secrets)
    print(decrypt_message)
    b'xyzxyzxyzxyzxyzxyzxyzxyzxyzxyz'

7zip


完成用例的另一种方法是使用 7zip and the Python subprocess 模块。对于这个例子,我展示了如何创建 in-memory 文件系统,加密数据,将数据和加密密钥写入内存中的目录,将这些文件写入磁盘上加密密码保护的 7zip 文件。我还展示了如何从此 7zip 存档中提取文件并解密 secret data.

import fs
import os
import base64
from subprocess import Popen, PIPE
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

# create in-memory file system
mem_fs = fs.open_fs('mem://')
mem_fs.makedir('hidden_dir')

# password for the cryptographic process
password = b"password"

# random salt for the cryptographic process
salt = os.urandom(16)

# key derivation function (KDF) for the cryptographic process
kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    iterations=390000,
)

# create the encryption key for the cryptographic process
encryption_key = base64.urlsafe_b64encode(kdf.derive(password))

# https://cryptography.io/en/latest/fernet/
f = Fernet(encryption_key)

# generate data
secret_data = b'xyz' * 10

encrypted_data = f.encrypt(secret_data)

# password for the zip file
secret_password = b'super secret password'

# write encryption key to an in-memory file
with mem_fs.open('hidden_dir/encryption_key.txt', 'wb') as in_file_in_memory:
    in_file_in_memory.write(encryption_key)
    in_file_in_memory.close()

# write encrypted data to an in-memory file
with mem_fs.open('hidden_dir/encrypted.txt', 'wb') as in_file_in_memory:
    in_file_in_memory.write(encrypted_data)
    in_file_in_memory.close()

# Create encrypted password protected 7ZIP file on disk
file_names = ['encryption_key.txt', 'encrypted.txt']
data_elements = [mem_fs.readbytes('hidden_dir/encryption_key.txt'), mem_fs.readbytes('hidden_dir/encrypted.txt')]
for file_name, data_element in zip(file_names, data_elements):
    args = [f'{full_path}/7zz', 'a', '-mem=AES256', '-y', f'-p{secret_password}', f'-si{file_name}', f'{zip_archive_name}']
    proc_zip = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    proc_zip.stdin.write(data_element)
    proc_zip.communicate()

# Unzip encrypted password protected 7ZIP file from disk
outfile_directory_name = 'test_files'
args = [f'{full_path}/7zz', "x", f'-p{secret_password}', f"{zip_archive_name}", f'-o{outfile_directory_name}']
proc_unzip = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
proc_unzip.communicate()

# decrypt data from disk
with open(f'{full_path}/{outfile_directory_name}/encryption_key.txt', 'rb') as key_file:
    with open(f'{full_path}/{outfile_directory_name}/encrypted.txt', 'rb') as text_file:
        f = Fernet(key_file.read())
        print(f.decrypt(text_file.read()))
        # output
        b'xyzxyzxyzxyzxyzxyzxyzxyzxyzxyz'