Zip-файл Python 3, добавляющий файлы в зашифрованный 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")

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

TL;DR: z.write() не выдает исключения, как и z.setpassword() или что-то еще, связанное с zip-файлом, при вводе неправильного пароля и добровольно добавляет файлы, несмотря ни на что. Я ожидал получить BadPasswordForFile?

Есть какой-либо способ сделать это?

Спасибо.


person Maybe LB Did It    schedule 05.12.2020    source источник
comment
Можете попробовать сделать минимальный воспроизводимый пример? Это немного трудно понять. Также. Я не уверен, почему вы делаете импорт повсюду. Придерживайтесь импорта в верхней части вашего кода, нет причин импортировать в произвольные места в коде.   -  person Tom Myddeltyn    schedule 06.12.2020
comment
Из документации. Когда вы выполняете функцию setpassword(pwd), она устанавливает пароль, который будет использоваться по умолчанию с другими командами, поэтому, возможно, это работает для вас или вы ожидаете другого результата?   -  person Tom Myddeltyn    schedule 06.12.2020
comment
@TomMyddeltyn Я боялся, что за этим будет немного сложно следить. Я ожидаю, что графический интерфейс отобразит метку с сообщением об ошибке, если пользователь введет неправильный пароль, но ZIP-файл, похоже, может редактировать зашифрованный ZIP-файл независимо от того, какой пароль вводит пользователь.   -  person Maybe LB Did It    schedule 06.12.2020
comment
@TomMyddeltyn Я обновил пост минималистичным кодом. Я не уверен, что я просто глуп, но я ожидаю ошибки BadPasswordForFile, а не просто... работать.   -  person Maybe LB Did It    schedule 06.12.2020
comment
Что происходит, когда вы открываете zip-файл проверенным zip-приложением? Я думаю, может быть, пароль только для распаковки и добавление произвольных файлов не проблема? Отсюда security.stackexchange.com/questions/33081 / Я думаю, у вас могут быть разные пароли для разных файлов в zip. Я думаю, ключом будет то, что происходит, когда вы пытаетесь разархивировать.   -  person Tom Myddeltyn    schedule 06.12.2020
comment
@TomMyddeltyn Я открыл его с помощью диспетчера архивов Ubuntu, и он открывается очень хорошо, а исходный файл там имеет замок, что означает, что я вообще не могу его открыть или коснуться. Файл, который я добавил с помощью приведенного выше кода, вообще не имеет замка, и я могу делать с ним все, что захочу. Это похоже на то, что файлы зашифрованы и защищены паролем, а не архив...   -  person Maybe LB Did It    schedule 06.12.2020
comment
Если я правильно помню, пароли устанавливаются для определенных файлов внутри zip-архива.   -  person Maarten Bodewes    schedule 06.12.2020
comment
@MaartenBodewes Спасибо, я посмотрю, смогу ли я найти обходной путь.   -  person Maybe LB Did It    schedule 06.12.2020


Ответы (2)


В документации к zipfile я нашел, что библиотека поддерживает расшифровку только с паролем. Он не может зашифровать. Таким образом, вы не сможете добавлять файлы с паролем.

Он поддерживает расшифровку зашифрованных файлов в ZIP-архивах, но в настоящее время не может создать зашифрованный файл. https://docs.python.org/3/library/zipfile.html

РЕДАКТИРОВАТЬ: Далее, изучая ошибки python Ошибка 34546: добавить поддержку шифрования в zip-файл, кажется, что для чтобы не увековечивать слабую схему паролей, которая используется в zip, они решили не включать ее.

Что-то, что вы могли бы сделать, это использовать subprocess для добавления файлов с паролем. Кроме того, если вы хотите сначала проверить введенный пароль, вы можете сделать что-то подобное, но вам нужно будет знать содержимое файла, потому что дешифрование будет успешно расшифровывать любой файл с любым паролем, результат открытого текста будет просто неверным.

Проблемы, которые вам предстоит решить:

  • Сравнение содержимого файла для проверки пароля
  • Обработка, когда файл уже существует в zip-файле
  • обработка, когда zip-файл уже существует И когда его нет.
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 в стандартный ввод subprocess.run с помощью input=. Я думаю, что что-то вроде pexpect может быть решением для этого, но я не тратил время на то, чтобы это сработало. См. здесь, например, как использовать pexpect для этого: Использовать подпроцесс для отправки пароля< / а> _

person Tom Myddeltyn    schedule 06.12.2020
comment
Однако я думаю, что, поскольку мы запускаем процесс как таковой, пароль не будет где-то легко захватить. Он тривиально доступен в списке процессов (например, ps aux); вот почему он считается небезопасным. - person AKX; 06.12.2020
comment
Я попытался изменить на res = subprocess.run(['zip', '-e', zipFilename, filename], input=zipPass), но, к сожалению, это не сработало. - person Tom Myddeltyn; 06.12.2020
comment
Я нашел более простой способ обойти это: я попросил пользователя ввести пароль, получил zipfile.namelist() файла и взял первый найденный файл. Затем он попытается извлечь этот файл с заданным паролем, а затем выдаст исключение, если пароль явно неверен. Хотя это на самом деле не работает с пустыми архивами и может вызывать ошибки для незашифрованных архивов, все должно работать нормально. - person Maybe LB Did It; 07.12.2020
comment
поймите, однако, что zip позволяет различным файлам иметь разные пароли, связанные с ними. Я также не уверен, почему это не поддерживает запись с паролями. Хотя это не на 100% безопасно, я не понимаю, почему это не реализовано. - person Tom Myddeltyn; 07.12.2020
comment
Да, я тоже рассматривал это как потенциальную проблему. Это просто то, что я должен записать в свою документацию. Я буду стараться изо всех сил, чтобы сделать это. - person Maybe LB Did It; 08.12.2020
comment
@TomMyddeltyn Спасибо!! - person Maybe LB Did It; 11.12.2020

После всех прекрасных ответов я нашел обходной путь на случай, если кому-то понадобится ответ!

Сначала я повторил попытку 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

Спасибо, ребята, за комментарии и ответы!

person Maybe LB Did It    schedule 11.12.2020