Есть ли простой способ генерирования (и проверки) контрольных сумм MD5 списка файлов в Python? (У меня есть небольшая программа, над которой я работаю, и я хотел бы подтвердить контрольные суммы файлов).
Хранение в Python облегчает управление кроссплатформенной совместимостью.
Александр
Если вам нужно решение с «индикатором выполнения» * или аналогичным (для очень больших файлов), рассмотрите это решение: stackoverflow.com/questions/1131220/…
Laurent LAPORTE
1
@kennytm Ссылка, которую вы предоставили, говорит об этом во втором абзаце: «Базовый алгоритм MD5 больше не считается безопасным» при описании md5sum. Вот почему программисты, которые заботятся о безопасности, не должны использовать это, по моему мнению.
Debug255
1
@ Debug255 Хороший и верный момент. Как md5sumи метод , описанный в этом вопросе SO следует избегать - это лучше использовать SHA-2 или SHA-3, если это возможно: en.wikipedia.org/wiki/Secure_Hash_Algorithms
Обратите внимание, что иногда вы не сможете разместить весь файл в памяти. В этом случае вам нужно будет последовательно прочитать куски по 4096 байт и передать их md5методу:
Примечание:hash_md5.hexdigest() вернет представление шестнадцатеричной строки для дайджеста, если вам просто нужно использовать упакованные байты return hash_md5.digest(), поэтому вам не нужно конвертировать обратно.
[(fname, hashlib.md5(file_as_bytes(open(fname,'rb'))).digest())for fname in fnamelst]
Напомним, однако, что MD5, как известно, поврежден и не должен использоваться для каких-либо целей, поскольку анализ уязвимостей может быть очень сложным, и анализ любого возможного будущего использования вашего кода может быть применен для решения проблем безопасности, невозможно. ИМХО, он должен быть полностью удален из библиотеки, чтобы все, кто его использует, были вынуждены обновляться. Итак, вот что вы должны сделать вместо этого:
[(fname, hashlib.sha256(file_as_bytes(open(fname,'rb'))).digest())for fname in fnamelst]
Если вам нужен всего лишь 128 бит, вы можете это сделать .digest()[:16].
Это даст вам список кортежей, каждый из которых содержит имя своего файла и его хэш.
Опять я сильно сомневаюсь в вашем использовании MD5. Вы должны по крайней мере использовать SHA1, и учитывая недавние недостатки, обнаруженные в SHA1 , вероятно, даже не это. Некоторые люди думают, что пока вы не используете MD5 для «криптографических» целей, все в порядке. Но вещи имеют тенденцию к тому, чтобы в конечном итоге оказаться шире, чем вы изначально ожидали, и ваш случайный анализ уязвимостей может оказаться совершенно ошибочным. Лучше всего просто привыкнуть использовать правильный алгоритм из ворот. Это просто набор другой связки букв и все. Это не так сложно.
Вот способ более сложный, но эффективный с точки зрения памяти :
Я только использую MD5, чтобы подтвердить, что файл не поврежден. Я не так обеспокоен тем, что его сломают.
Александр
87
@TheLifelessOne: И несмотря на страшные предупреждения, это очень хорошее применение MD5.
Президент Джеймс К. Полк
22
@GregS, @TheLifelessOne - Да, и следующее, что вы знаете, кто-то находит способ использовать этот факт в вашем приложении для того, чтобы файл был принят как не поврежденный, если это совсем не тот файл, который вы ожидаете. Нет, я поддерживаю свои страшные предупреждения. Я думаю, что MD5 должен быть удален или идти с предупреждениями об устаревании.
Всезнающий
10
Я бы, вероятно, использовал .hexdigest () вместо .digest () - людям легче читать - что является целью OP.
zbstof
21
Я использовал это решение, но оно неверно дало один и тот же хеш для двух разных файлов PDF. Решением было открыть файлы, указав двоичный режим, а именно: [(fname, hashlib.md5 (open (fname, 'rb' ) .read ()). Hexdigest ()) для fname в fnamelst] Это более связано в функцию open, чем в md5, но я подумал, что было бы полезно сообщить об этом, учитывая указанное выше требование кросс-платформенной совместимости (см. также: docs.python.org/2/tutorial/… ).
BlueCoder
34
Я явно не добавляю ничего принципиально нового, но добавил этот ответ, прежде чем приступил к комментированию статуса, плюс регионы кода проясняют ситуацию - во всяком случае, специально для ответа на вопрос @ Nemo из ответа Omnifarious:
Я немного подумал о контрольных суммах (пришел сюда в поисках предложений по размерам блоков, в частности), и обнаружил, что этот метод может быть быстрее, чем вы ожидаете. Взятие самого быстрого (но довольно типичного) timeit.timeitили /usr/bin/timeрезультата каждого из нескольких методов проверки контрольной суммы файла ок. 11MB:
$ ./sum_methods.py
crc32_mmap(filename)0.0241742134094
crc32_read(filename)0.0219960212708
subprocess.check_output(['cksum', filename])0.0553209781647
md5sum_mmap(filename)0.0286180973053
md5sum_read(filename)0.0311000347137
subprocess.check_output(['md5sum', filename])0.0332629680634
$ time md5sum /tmp/test.data.300k
d3fe3d5d4c2460b5daacc30c6efbc77f /tmp/test.data.300k
real 0m0.043s
user 0m0.032s
sys 0m0.010s
$ stat -c '%s'/tmp/test.data.300k11890400
Итак, похоже, что для Python и / usr / bin / md5sum требуется около 30 мс для файла размером 11 МБ. Соответствующая md5sumфункция ( md5sum_readв приведенном выше списке) очень похожа на функцию Omnifarious:
Конечно, они из одиночных прогонов ( mmapте, которые выполняются на несколько шагов быстрее, когда выполняется, по крайней мере, несколько десятков прогонов), и мой обычно получает дополнительный f.read(blocksize)после того, как буфер исчерпан, но это достаточно повторяется и показывает, что md5sumв командной строке не обязательно быстрее, чем реализация Python ...
РЕДАКТИРОВАТЬ: Извините за долгую задержку, не смотрел на это некоторое время, но чтобы ответить на вопрос @ EdRandall, я запишу реализацию Adler32. Тем не менее, я не проводил тесты для этого. По сути, это то же самое, что и CRC32: вместо вызовов init, update и digest все является zlib.adler32()вызовом:
Обратите внимание, что это должно начинаться с пустой строки, так как суммы Адлера действительно различаются, когда начинаются с нуля, в сравнении с их суммой "", то есть 1- 0вместо этого можно начать с CRC . AND-Ную требуется , чтобы сделать это 32-разрядное целое число без знака, который гарантирует , что он возвращает то же значение между версиями Python.
Не могли бы вы добавить пару строк, сравнивающих SHA1, а также zlib.adler32?
Эд Рэндалл
1
Приведенная выше функция md5sum () предполагает, что у вас есть права на запись в файл. Если вы замените «r + b» в вызове open () на «rb», он будет работать нормально.
import hashlib
with open("your_filename.txt","rb")as f:
file_hash = hashlib.md5()while chunk := f.read(8192):
file_hash.update(chunk)print(file_hash.digest())print(file_hash.hexdigest())# to get a printable str instead of bytes
Попробуйте использовать hashlib.blake2bвместо md5(просто заменить md5на blake2bв приведенном выше фрагменте). Это криптографически безопасно и быстрее, чем MD5.
Здравствуй! Пожалуйста, добавьте некоторые объяснения в ваш код, почему это решение проблемы. Кроме того, этот пост довольно старый, поэтому вы также должны добавить некоторую информацию о том, почему ваше решение добавляет то, что другие еще не рассмотрели.
d_kennetz
1
Это еще один неэффективный способ памяти
Erik Aronesty
-2
Я думаю, что полагаться на пакет invoke и двоичный файл md5sum немного удобнее, чем на подпроцесс или пакет md5.
md5sum
?md5sum
. Вот почему программисты, которые заботятся о безопасности, не должны использовать это, по моему мнению.md5sum
и метод , описанный в этом вопросе SO следует избегать - это лучше использовать SHA-2 или SHA-3, если это возможно: en.wikipedia.org/wiki/Secure_Hash_AlgorithmsОтветы:
Вы можете использовать hashlib.md5 ()
Обратите внимание, что иногда вы не сможете разместить весь файл в памяти. В этом случае вам нужно будет последовательно прочитать куски по 4096 байт и передать их
md5
методу:Примечание:
hash_md5.hexdigest()
вернет представление шестнадцатеричной строки для дайджеста, если вам просто нужно использовать упакованные байтыreturn hash_md5.digest()
, поэтому вам не нужно конвертировать обратно.источник
Есть способ, который довольно неэффективно с памятью .
отдельный файл:
список файлов:
Напомним, однако, что MD5, как известно, поврежден и не должен использоваться для каких-либо целей, поскольку анализ уязвимостей может быть очень сложным, и анализ любого возможного будущего использования вашего кода может быть применен для решения проблем безопасности, невозможно. ИМХО, он должен быть полностью удален из библиотеки, чтобы все, кто его использует, были вынуждены обновляться. Итак, вот что вы должны сделать вместо этого:
Если вам нужен всего лишь 128 бит, вы можете это сделать
.digest()[:16]
.Это даст вам список кортежей, каждый из которых содержит имя своего файла и его хэш.
Опять я сильно сомневаюсь в вашем использовании MD5. Вы должны по крайней мере использовать SHA1, и учитывая недавние недостатки, обнаруженные в SHA1 , вероятно, даже не это. Некоторые люди думают, что пока вы не используете MD5 для «криптографических» целей, все в порядке. Но вещи имеют тенденцию к тому, чтобы в конечном итоге оказаться шире, чем вы изначально ожидали, и ваш случайный анализ уязвимостей может оказаться совершенно ошибочным. Лучше всего просто привыкнуть использовать правильный алгоритм из ворот. Это просто набор другой связки букв и все. Это не так сложно.
Вот способ более сложный, но эффективный с точки зрения памяти :
И, опять же, поскольку MD5 сломан и больше не должен использоваться:
Опять же, вы можете поставить
[:16]
после вызова,hash_bytestr_iter(...)
если вы хотите только 128 битов дайджеста.источник
Я явно не добавляю ничего принципиально нового, но добавил этот ответ, прежде чем приступил к комментированию статуса, плюс регионы кода проясняют ситуацию - во всяком случае, специально для ответа на вопрос @ Nemo из ответа Omnifarious:
Я немного подумал о контрольных суммах (пришел сюда в поисках предложений по размерам блоков, в частности), и обнаружил, что этот метод может быть быстрее, чем вы ожидаете. Взятие самого быстрого (но довольно типичного)
timeit.timeit
или/usr/bin/time
результата каждого из нескольких методов проверки контрольной суммы файла ок. 11MB:Итак, похоже, что для Python и / usr / bin / md5sum требуется около 30 мс для файла размером 11 МБ. Соответствующая
md5sum
функция (md5sum_read
в приведенном выше списке) очень похожа на функцию Omnifarious:Конечно, они из одиночных прогонов (
mmap
те, которые выполняются на несколько шагов быстрее, когда выполняется, по крайней мере, несколько десятков прогонов), и мой обычно получает дополнительныйf.read(blocksize)
после того, как буфер исчерпан, но это достаточно повторяется и показывает, чтоmd5sum
в командной строке не обязательно быстрее, чем реализация Python ...РЕДАКТИРОВАТЬ: Извините за долгую задержку, не смотрел на это некоторое время, но чтобы ответить на вопрос @ EdRandall, я запишу реализацию Adler32. Тем не менее, я не проводил тесты для этого. По сути, это то же самое, что и CRC32: вместо вызовов init, update и digest все является
zlib.adler32()
вызовом:Обратите внимание, что это должно начинаться с пустой строки, так как суммы Адлера действительно различаются, когда начинаются с нуля, в сравнении с их суммой
""
, то есть1
-0
вместо этого можно начать с CRC .AND
-Ную требуется , чтобы сделать это 32-разрядное целое число без знака, который гарантирует , что он возвращает то же значение между версиями Python.источник
В Python 3.8+ вы можете сделать
Попробуйте использовать
hashlib.blake2b
вместоmd5
(просто заменитьmd5
наblake2b
в приведенном выше фрагменте). Это криптографически безопасно и быстрее, чем MD5.источник
:=
Оператор является «оператор присваивания» (новый Python 3.8+); это позволяет назначать значения внутри большего выражения; больше информации здесь: docs.python.org/3/whatsnew/3.8.html#assignment-expressionsисточник
Я думаю, что полагаться на пакет invoke и двоичный файл md5sum немного удобнее, чем на подпроцесс или пакет md5.
Это, конечно, предполагает, что у вас есть invoke и md5sum установлены.
источник
path
это пользовательский путь, это позволит любому пользователю выполнять произвольные команды bash в вашей системе.