Bezpieczne haszowanie za pomocą Python Hashlib

Ten samouczek nauczy Cię, jak tworzyć bezpieczne skróty przy użyciu wbudowanej funkcjonalności modułu hashlib Pythona.

Zrozumienie znaczenia haszowania i sposobu programowego obliczania bezpiecznych skrótów może być pomocne — nawet jeśli nie zajmujesz się bezpieczeństwem aplikacji. Ale dlaczego?

Cóż, pracując nad projektami w Pythonie, prawdopodobnie natkniesz się na przypadki, w których obawiasz się przechowywania haseł i innych poufnych informacji w bazach danych lub plikach kodu źródłowego. W takich przypadkach bezpieczniej jest uruchomić algorytm mieszający na poufnych informacjach i przechowywać skrót zamiast informacji.

W tym przewodniku omówimy, czym jest haszowanie i czym różni się ono od szyfrowania. Omówimy również właściwości bezpiecznych funkcji skrótu. Następnie użyjemy typowych algorytmów haszujących do obliczenia skrótu zwykłego tekstu w Pythonie. W tym celu użyjemy wbudowanego modułu hashlib.

To wszystko i więcej, zaczynajmy!

Co to jest haszowanie?

Proces haszowania pobiera ciąg komunikatów i daje wyjście o stałej długości zwane haszem. Oznacza to, że długość hasza wyjściowego dla danego algorytmu haszującego jest stała – niezależnie od długości wejścia. Ale czym to się różni od szyfrowania?

W przypadku szyfrowania wiadomość lub zwykły tekst jest szyfrowany przy użyciu algorytmu szyfrowania, który daje zaszyfrowane dane wyjściowe. Następnie możemy uruchomić algorytm deszyfrowania na zaszyfrowanych danych wyjściowych, aby odzyskać ciąg wiadomości.

Jednak haszowanie działa inaczej. Właśnie dowiedzieliśmy się, że proces szyfrowania jest odwracalny, ponieważ można przejść od zaszyfrowanej wiadomości do niezaszyfrowanej wiadomości i odwrotnie.

W przeciwieństwie do szyfrowania, haszowanie nie jest procesem odwracalnym, co oznacza, że ​​nie możemy przejść od haszowania do wiadomości wejściowej.

Właściwości funkcji mieszających

Przyjrzyjmy się szybko niektórym właściwościom, które powinny spełniać funkcje skrótu:

  • Deterministyczny: Funkcje skrótu są deterministyczne. Biorąc pod uwagę wiadomość m, skrót m jest zawsze taki sam.
  • Preimage Resistant: Omówiliśmy to już, gdy powiedzieliśmy, że mieszanie nie jest operacją odwracalną. Właściwość odporności obrazu wstępnego stwierdza, że ​​znalezienie wiadomości m z wyjściowego skrótu jest niewykonalne.
  • Odporność na kolizje: Znalezienie dwóch różnych ciągów komunikatów m1 i m2 powinno być trudne (lub niewykonalne obliczeniowo) tak, aby hash m1 był równy hashowi m2. Właściwość ta nazywana jest odpornością na zderzenia.
  • Second Preimage Resistant: Oznacza to, że biorąc pod uwagę wiadomość m1 i odpowiadający jej hash m2, niemożliwe jest znalezienie innej wiadomości m2 takiej, że hash(m1) = hash(m2).

Moduł hashlib Pythona

Wbudowany w Pythonie moduł hashlib zapewnia implementacje kilku algorytmów mieszania i skrótu wiadomości, w tym algorytmów SHA i MD5.

Aby użyć konstruktorów i wbudowanych funkcji z modułu hashlib Pythona, możesz zaimportować go do swojego środowiska pracy w następujący sposób:

import hashlib

Moduł hashlib zapewnia stałe algorytmy_dostępne i algorytmy_gwarantowane, które oznaczają zestaw algorytmów, których implementacje są odpowiednio dostępne i gwarantowane na platformie.

Dlatego algorytmy_gwarantowane są podzbiorem algorytmów_dostępnych.

Uruchom Python REPL, zaimportuj hashlib i uzyskaj dostęp do stałych algorytmów_dostępnych i algorytmów_gwarantowanych:

>>> 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'}

Widzimy, że algorytms_gwarantowane jest rzeczywiście podzbiorem algorytmów_dostępnych

Jak tworzyć obiekty mieszające w Pythonie

Następnie nauczmy się tworzyć obiekty haszujące w Pythonie. Obliczymy skrót SHA256 ciągu wiadomości za pomocą następujących metod:

  • Ogólny konstruktor new().
  • Konstruktory specyficzne dla algorytmu

Korzystanie z konstruktora new().

Zainicjujmy ciąg komunikatu:

>>> message = "newsblog.pl is awesome!"

Aby utworzyć instancję obiektu hash, możemy użyć konstruktora new() i przekazać nazwę algorytmu, jak pokazano:

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

Możemy teraz wywołać metodę update() na obiekcie hash z ciągiem wiadomości jako argumentem:

>>> sha256_hash.update(message)

Jeśli to zrobisz, napotkasz błąd, ponieważ algorytmy haszujące mogą działać tylko z ciągami bajtów.

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

Aby uzyskać zakodowany ciąg znaków, można wywołać metodę encode() na łańcuchu metody, a następnie użyć jej w wywołaniu metody update() . Po wykonaniu tej czynności możesz wywołać metodę hexdigest() w celu uzyskania skrótu sha256 odpowiadającego ciągowi komunikatu.

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

Zamiast kodować łańcuch komunikatów za pomocą metody encode() , możesz również zdefiniować go jako ciąg bajtów, poprzedzając go literą b w następujący sposób:

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

Otrzymany hash jest taki sam jak poprzedni, co potwierdza deterministyczny charakter funkcji haszujących.

Ponadto niewielka zmiana w ciągu komunikatu powinna spowodować drastyczną zmianę skrótu (znaną również jako „efekt lawiny”).

Aby to zweryfikować, zmieńmy „a” w „niesamowite” na „A” i obliczmy skrót:

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

Widzimy, że hash zmienia się całkowicie.

Korzystanie z konstruktora specyficznego dla algorytmu

W poprzednim przykładzie użyliśmy ogólnego konstruktora new() i przekazaliśmy „SHA256” jako nazwę algorytmu do utworzenia obiektu haszującego.

Zamiast tego możemy również użyć konstruktora sha256(), jak pokazano na rysunku:

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

Wyjściowy skrót jest identyczny z haszem, który otrzymaliśmy wcześniej dla ciągu wiadomości „newsblog.pl jest niesamowity!”.

Badanie atrybutów obiektów mieszających

Obiekty hash mają kilka przydatnych atrybutów:

  • Atrybut digest_size określa rozmiar skrótu w bajtach. Na przykład algorytm SHA256 zwraca 256-bitowy skrót, co odpowiada 32 bajtom
  • Atrybut block_size odnosi się do rozmiaru bloku używanego w algorytmie mieszania.
  • Atrybut name to nazwa algorytmu, którego możemy użyć w konstruktorze new(). Wyszukiwanie wartości tego atrybutu może być pomocne, gdy obiekty skrótu nie mają opisowych nazw.

Możemy sprawdzić te atrybuty dla utworzonego wcześniej obiektu sha256_hash:

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

Następnie przyjrzyjmy się kilku interesującym zastosowaniom haszowania przy użyciu modułu hashlib Pythona.

Praktyczne przykłady haszowania

Weryfikacja integralności oprogramowania i plików

Jako programiści cały czas pobieramy i instalujemy pakiety oprogramowania. Dzieje się tak niezależnie od tego, czy pracujesz na dystrybucji Linuksa, czy na systemie Windows lub Mac.

Jednak niektóre kopie lustrzane pakietów oprogramowania mogą nie być godne zaufania. Możesz znaleźć skrót (lub sumę kontrolną) obok linku do pobrania. Możesz zweryfikować integralność pobranego oprogramowania, obliczając skrót i porównując go z oficjalnym haszem.

Można to zastosować również do plików na twoim komputerze. Nawet najmniejsza zmiana zawartości pliku drastycznie zmieni hash, możesz sprawdzić, czy plik został zmodyfikowany, weryfikując hash.

Oto prosty przykład. Utwórz plik tekstowy „my_file.txt” w katalogu roboczym i dodaj do niego trochę treści.

$ 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.

Następnie możesz otworzyć plik w trybie odczytu binarnego („rb”), wczytać zawartość pliku i obliczyć skrót SHA256, jak pokazano:

>>> 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()

Tutaj zmienna original_hash jest hashem pliku „my_file.txt” w jego bieżącym stanie.

>>> original_hash
# Output: '53bfd0551dc06c4515069d1f0dc715d002d451c8799add29f3e5b7328fda9f8f'

Teraz zmodyfikuj plik „my_file.txt”. Możesz usunąć dodatkowe wiodące spacje przed słowem „going”. 🙂

Oblicz hash jeszcze raz i zapisz go w zmiennej 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()

Następnie możesz dodać prostą instrukcję assert, która sprawdza, czy obliczony_hash jest równy oryginalnemu haszowi.

>>> assert computed_hash == original_hash

Jeśli plik jest modyfikowany (co w tym przypadku jest prawdą), powinieneś otrzymać błąd AssertionError:

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

Możesz używać haszowania podczas przechowywania poufnych informacji, takich jak hasła w bazach danych. Możesz także użyć haszowania w uwierzytelnianiu hasła podczas łączenia się z bazami danych. Zweryfikuj skrót wprowadzonego hasła względem skrótu poprawnego hasła.

Wniosek

Mam nadzieję, że ten samouczek pomógł ci dowiedzieć się, jak generować bezpieczne skróty w Pythonie. Oto najważniejsze wnioski:

  • Moduł hashlib Pythona zapewnia gotowe do użycia implementacje kilku algorytmów haszujących. Możesz uzyskać listę algorytmów gwarantowanych na swojej platformie za pomocą hashlib.algorithms_guaranteed.
  • Aby utworzyć obiekt hash, możesz użyć ogólnego konstruktora new() o składni: hashlib.new(“algo-name”). Alternatywnie możesz użyć konstruktorów odpowiadających określonym algorytmom haszującym, na przykład: hashlib.sha256() dla skrótu SHA 256.
  • Po zainicjowaniu ciągu komunikatów do haszowania i obiektu hash można wywołać metodę update() na obiekcie hash, a następnie metodę hexdigest() w celu uzyskania skrótu.
  • Haszowanie może się przydać podczas sprawdzania integralności artefaktów oprogramowania i plików, przechowywania poufnych informacji w bazach danych i nie tylko.

Następnie dowiedz się, jak zakodować generator losowych haseł w Pythonie.