Сигурно хеширане с Python Hashlib

Този урок ще ви научи как да създавате сигурни хешове, като използвате вградена функционалност от модула hashlib на Python.

Разбирането на значението на хеширането и как програмно да се изчисляват сигурни хешове може да бъде полезно – дори ако не работите в областта на сигурността на приложенията. Но защо?

Е, когато работите върху проекти на Python, вероятно ще срещнете случаи, в които сте загрижени за съхраняването на пароли и друга чувствителна информация в бази данни или файлове с изходен код. В такива случаи е по-безопасно да стартирате алгоритъма за хеширане върху поверителна информация и да съхранявате хеша вместо информацията.

В това ръководство ще разгледаме какво е хеширане и как се различава от криптирането. Ще разгледаме и свойствата на защитените хеш функции. След това ще използваме общи алгоритми за хеширане, за да изчислим хеша на обикновен текст в Python. За да направим това, ще използваме вградения модул hashlib.

За всичко това и още, нека да започнем!

Какво е хеширане?

Процесът на хеширане приема низ от съобщения и дава изход с фиксирана дължина, наречен хеш. Това означава, че дължината на изходния хеш за даден алгоритъм за хеширане е фиксирана – независимо от дължината на входа. Но как се различава от криптирането?

При криптирането съобщението или обикновеният текст се криптират с помощта на алгоритъм за криптиране, който дава криптиран изход. След това можем да стартираме алгоритъма за дешифриране на шифрования изход, за да върнем низа на съобщението.

Хеширането обаче работи по различен начин. Току-що научихме, че процесът на криптиране е обратим, тъй като можете да преминете от криптирано съобщение към некриптирано съобщение и обратно.

За разлика от криптирането, хеширането не е обратим процес, което означава, че не можем да преминем от хеша към входното съобщение.

Свойства на хеш функциите

Нека бързо да прегледаме някои свойства, на които трябва да отговарят хеш функциите:

  • Детерминистични: Хеш функциите са детерминирани. При дадено съобщение m, хешът на m винаги е един и същ.
  • Устойчивост на предварително изображение: Вече разгледахме това, когато казахме, че хеширането не е обратима операция. Свойството за съпротивление на предварително изображение заявява, че е невъзможно да се намери съобщението m от изходния хеш.
  • Устойчив на сблъсък: Трябва да е трудно (или изчислително неосъществимо) да се намерят два различни низа на съобщения m1 и m2, така че хешът на m1 да е равен на хеша на m2. Това свойство се нарича устойчивост на сблъсък.
  • Устойчивост на второ предварително изображение: Това означава, че при дадено съобщение m1 и съответния хеш m2 е невъзможно да се намери друго съобщение m2, такова че hash(m1) = hash(m2).
  Какво е времето за реакция на монитора и защо е важно?

Модул hashlib на Python

Вграденият в Python модул hashlib осигурява реализации на няколко алгоритми за хеширане и извличане на съобщения, включително алгоритмите SHA и MD5.

За да използвате конструкторите и вградените функции от модула hashlib на Python, можете да го импортирате във вашата работна среда по следния начин:

import hashlib

Модулът hashlib предоставя константите algorithms_available и algorithms_guaranteed, които обозначават набора от алгоритми, чиито реализации са налични и съответно гарантирани на платформа.

Следователно algorithms_guaranteed е подмножество от algorithms_available.

Стартирайте Python REPL, импортирайте hashlib и отворете константите algorithms_available и algorithms_guaranteed:

>>> hashlib.algorithms_available
# Output
{'md5', 'md5-sha1', 'sha3_256', 'shake_128', 'sha384', 'sha512_256', 'sha512', 'md4', 
'shake_256', 'whirlpool', 'sha1', 'sha3_512', 'sha3_384', 'sha256', 'ripemd160', 'mdc2', 
'sha512_224', 'blake2s', 'blake2b', 'sha3_224', 'sm3', 'sha224'}
>>> hashlib.algorithms_guaranteed
# Output
{'md5', 'shake_256', 'sha3_256', 'shake_128', 'blake2b', 'sha3_224', 'sha3_384', 
'sha384', 'sha256', 'sha1', 'sha3_512', 'sha512', 'blake2s', 'sha224'}

Виждаме, че algorithms_guaranteed наистина е подмножество от algorithms_available

Как да създадете хеш обекти в Python

След това нека научим как да създаваме хеш обекти в Python. Ще изчислим SHA256 хеша на низ на съобщение, като използваме следните методи:

  • Общият конструктор new().
  • Специфични за алгоритъма конструктори

Използване на конструктора new().

Нека инициализираме низа на съобщението:

>>> message = "pctechbg.net is awesome!"

За да инстанцираме хеш обекта, можем да използваме конструктора new() и да предадем името на алгоритъма, както е показано:

>>> sha256_hash = hashlib.new("SHA256")

Вече можем да извикаме метода update() на хеш обекта с низа на съобщението като аргумент:

>>> sha256_hash.update(message)

Ако го направите, ще попаднете на грешка, тъй като алгоритмите за хеширане могат да работят само с байтови низове.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Unicode-objects must be encoded before hashing

За да получите кодирания низ, можете да извикате метода encode() на низа на метода и след това да го използвате в извикването на метода update(). След като направите това, можете да извикате метода hexdigest(), за да получите хеша sha256, съответстващ на низа на съобщението.

sha256_hash.update(message.encode())
sha256_hash.hexdigest()
# Output:'b360c77de704ad8f02af963d7da9b3bb4e0da6b81fceb4c1b36723e9d6d9de3d'

Вместо да кодирате низа на съобщението с помощта на метода encode(), можете също да го дефинирате като низ от байтове, като поставите пред низа b по следния начин:

message = b"pctechbg.net is awesome!"
sha256_hash.update(message)
sha256_hash.hexdigest()
# Output: 'b360c77de704ad8f02af963d7da9b3bb4e0da6b81fceb4c1b36723e9d6d9de3d'

Полученият хеш е същият като предишния хеш, което потвърждава детерминистичния характер на хеш функциите.

  „OK Google“ става много по-сигурен на заключени телефони

В допълнение, малка промяна в низа на съобщението трябва да доведе до драстична промяна на хеша (известен също като „лавинен ефект“).

За да проверим това, нека променим „a“ в „awesome“ на „A“ и изчислим хеша:

message = "pctechbg.net is Awesome!"
h1 = hashlib.new("SHA256")
h1.update(message.encode())
h1.hexdigest()
# Output: '3c67f334cc598912dc66464f77acb71d88cfd6c8cba8e64a7b749d093c1a53ab'

Виждаме, че хешът се променя напълно.

Използване на конструктора, специфичен за алгоритъма

В предишния пример използвахме общия конструктор new() и предадохме „SHA256“ като име на алгоритъма за създаване на хеш обекта.

Вместо да го правим, можем също да използваме конструктора sha256(), както е показано:

sha256_hash = hashlib.sha256()
message= "pctechbg.net is awesome!"
sha256_hash.update(message.encode())
sha256_hash.hexdigest()
# Output: 'b360c77de704ad8f02af963d7da9b3bb4e0da6b81fceb4c1b36723e9d6d9de3d'

Изходният хеш е идентичен с хеша, който получихме по-рано за низа на съобщението „pctechbg.net е страхотен!“.

Изследване на атрибутите на хеш обектите

Хеш обектите имат няколко полезни атрибута:

  • Атрибутът digest_size обозначава размера на дайджеста в байтове. Например алгоритъмът SHA256 връща 256-битов хеш, който е еквивалентен на 32 байта
  • Атрибутът block_size се отнася до размера на блока, използван в алгоритъма за хеширане.
  • Атрибутът name е името на алгоритъма, който можем да използваме в конструктора new(). Търсенето на стойността на този атрибут може да бъде полезно, когато хеш обектите нямат описателни имена.

Можем да проверим тези атрибути за обекта sha256_hash, който създадохме по-рано:

>>> sha256_hash.digest_size
32
>>> sha256_hash.block_size
64
>>> sha256_hash.name
'sha256'

След това нека разгледаме някои интересни приложения на хеширане с помощта на модула hashlib на Python.

Практически примери за хеширане

Проверка на целостта на софтуера и файловете

Като разработчици, ние изтегляме и инсталираме софтуерни пакети през цялото време. Това е вярно, независимо дали работите върху дистрибуцията на Linux или на Windows или Mac.

Някои огледални сървъри за софтуерни пакети обаче може да не са надеждни. Можете да намерите хеша (или контролната сума) до връзката за изтегляне. И можете да проверите целостта на изтегления софтуер, като изчислите хеша и го сравните с официалния хеш.

  Как да промените съотношението на страните в iMovie

Това може да се приложи и към файлове на вашата машина. Дори и най-малката промяна в съдържанието на файла ще промени драстично хеша, можете да проверите дали даден файл е бил модифициран, като проверите хеша.

Ето един прост пример. Създайте текстов файл ‘my_file.txt’ в работната директория и добавете малко съдържание към него.

$ cat my_file.txt
This is a sample text file.
We are  going to compute the SHA256 hash of this text file and also
check if the file has been modified by
recomputing the hash.

След това можете да отворите файла в двоичен режим за четене (‘rb’), да прочетете съдържанието на файла и да изчислите SHA256 хеша, както е показано:

>>> import hashlib
>>> with open("my_file.txt","rb") as file:
...     file_contents = file.read()
...     sha256_hash = hashlib.sha256()
...     sha256_hash.update(file_contents)
...     original_hash = sha256_hash.hexdigest()

Тук променливата original_hash е хешът на ‘my_file.txt’ в текущото му състояние.

>>> original_hash
# Output: '53bfd0551dc06c4515069d1f0dc715d002d451c8799add29f3e5b7328fda9f8f'

Сега променете файла „my_file.txt“. Можете да премахнете допълнителното начално празно пространство преди думата „отивам“. 🙂

Изчислете хеша отново и го запазете в променливата computed_hash.

>>> import hashlib
>>> with open("my_file.txt","rb") as file:
...     file_contents = file.read()
...     sha256_hash = hashlib.sha256()
...     sha256_hash.update(file_contents)
...     computed_hash = sha256_hash.hexdigest()

След това можете да добавите прост оператор assert, който потвърждава дали computed_hash е равен на original_hash.

>>> assert computed_hash == original_hash

Ако файлът е модифициран (което е вярно в този случай), трябва да получите AssertionError:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

Можете да използвате хеширане, когато съхранявате чувствителна информация, като пароли в бази данни. Можете също да използвате хеширане при удостоверяване на парола, когато се свързвате с бази данни. Валидирайте хеша на въведената парола спрямо хеша на правилната парола.

Заключение

Надявам се, че този урок ви е помогнал да научите за генерирането на защитени хешове с Python. Ето основните изводи:

  • Модулът hashlib на Python предоставя готови за използване реализации на няколко алгоритъма за хеширане. Можете да получите списъка с алгоритми, гарантирани на вашата платформа, като използвате hashlib.algorithms_guaranteed.
  • За да създадете хеш обект, можете да използвате общия конструктор new() със синтаксис: hashlib.new(“algo-name”). Като алтернатива можете да използвате конструкторите, съответстващи на специфичните алгоритми за хеширане, като така: hashlib.sha256() за SHA 256 хеш.
  • След като инициализирате низа на съобщението за хеширане и хеш обекта, можете да извикате метода update() на хеш обекта, последван от метода hexdigest(), за да получите хеша.
  • Хеширането може да бъде полезно при проверка на целостта на софтуерни артефакти и файлове, съхраняване на чувствителна информация в бази данни и др.

След това научете как да кодирате генератор на произволни пароли в Python.