zipfile:将文件添加到加密的 zip

zipfile: Adding files to encrypted zip

我有一个加密的 ZIP 文件,出于某种原因,我输入的任何密码似乎都无关紧要,因为它无论如何都可以将文件添加到存档中。我检查了任何被忽略的异常或任何东西,但似乎没有什么是相当明显的。

我在下面发布了极简代码:

import zipfile

z = zipfile.ZipFile('test.zip', 'a') #Set zipfile object
zipPass = str(input("Please enter the zip password: "))
zipPass = bytes(zipPass, encoding='utf-8')
z.setpassword(zipPass) #Set password
z.write("test.txt")

我不确定我在这里遗漏了什么,但我在 zipfile 中寻找任何可以处理加密的 zipfile 并使用密码将文件添加到其中的东西,因为我唯一拥有的是``z.setpassword()` 函数在这里似乎不起作用。

TL;DR:z.write() 不会抛出异常,z.setpassword() 或任何 zipfile 相关的东西在输入不正确的密码时也不会抛出异常,并且无论如何都愿意添加文件。我期待得到 BadPasswordForFile.

有什么办法吗?

我在 zipfile 的文档中发现,该库只支持 解密 密码。它不能加密。所以你将无法添加带密码的文件。

It supports decryption of encrypted files in ZIP archives, but it currently cannot create an encrypted file. https://docs.python.org/3/library/zipfile.html

编辑:此外,查看 python 错误 Issue 34546: Add encryption support to zipfile 似乎为了不使 zip 中使用的弱密码方案永久存在,他们选择不包含它。

您可以使用 subprocess 添加带密码的文件。 此外,如果你想先“验证”输入的密码,你可以这样做,但你必须知道文件的内容,因为解密会愉快地用任何密码解密任何文件,明文结果将不是正确。

您必须解决的问题:

  • 比较文件内容以验证密码
  • 压缩文件中已存在文件时的处理
  • 压缩文件已经存在和不存在时的处理。
import subprocess
import zipfile

def zip_file(zipFilename, filename):
    zipPass = str(input("Please enter the zip password: "))
    zipPass = bytes(zipPass, encoding='utf-8')

    #If there is a file that we know the plain-text (or original binary)
    #TODO: handle fipFilename not existing.
    validPass=False
    with zipfile.ZipFile(zipFilename, 'r') as zFile:
        zFile.setpassword(zipPass)
        with zFile.open('known.txt') as knownFile:
            #TODO: compare contents of known.txt with actual
            validPass=True

    #Next to add file with password cannot use zipfile because password not supported
    # Note this is a linux only solution, os dependency will need to be checked
    #if compare was ok, then valid password?
    if not validPass:
        print('Invalid Password')
    else:
        #TODO: handle zipfile not-exist and existing may have to pass
        #      different flags.
        #TODO: handle filename existing in zipFilename
        #WARNING the linux manual page for 'zip' states -P is UNSECURE. 
        res = subprocess.run(['zip', '-e', '-P', zipPass, zipFilename, filename])
        #TODO: Check res for success or failure.

编辑: 我研究了用 -P 修复整个“暴露密码”的问题。不幸的是,这很重要。您不能简单地将 zipPass 写入带有 input=subprocess.run 的标准输入。我认为 pexpect 之类的东西可能是解决此问题的方法,但我还没有花时间来完成这项工作。有关如何使用 pexpect 完成此操作的示例,请参见此处:Use subprocess to send a password_

在所有可爱的回复之后,我确实找到了一个解决方法,以防万一有人需要答案!

我首先重试了 z.testzip(),它确实捕获了错误的密码,但在看到它不可靠之后(显然哈希冲突允许错误密码以某种方式匹配小哈希),我决定使用密码,提取它在存档中看到的第一个文件,然后再提取它。如果有效,请删除提取的文件,如果无效,则没有任何危害。

代码如下:

try:
    z = zipfile.ZipFile(fileName, 'a') #Set zipfile object
    zipPass = bytes(zipPass, encoding='utf-8') #Str to Bytes
    z.setpassword(zipPass) #Set password
    filesInArray = z.namelist() #Get all files
    testfile = filesInArray[0] #First archive in list
    z.extract(testfile, pwd=zipPass) #Extract first file
    os.remove(testfile) #remove file if successfully extracted
except Exception as e:
    print("Exception occurred: ",repr(e))
    return None #Return to mainGUI - this exits the function without further processing

谢谢大家的评论和回答!