Сигурно хеширање са Питхон Хасхлиб-ом

Ovaj vodič će vam objasniti kako se kreiraju sigurni heševi koristeći ugrađene funkcije Python-ovog modula `hashlib`.

Razumevanje važnosti heširanja i načina na koji se programski izračunavaju sigurni heševi može biti korisno, čak i ako se ne bavite bezbednošću aplikacija. Zašto je to tako?

Kada radite na Python projektima, često ćete se susretati sa situacijama u kojima je važno zaštititi lozinke i druge osetljive podatke u bazama podataka ili datotekama izvornog koda. U takvim slučajevima, sigurnije je primeniti algoritam heširanja na osetljive informacije i sačuvati heš umesto samih informacija.

U ovom vodiču ćemo razmotriti šta je heširanje i po čemu se razlikuje od enkripcije. Takođe ćemo objasniti svojstva sigurnih heš funkcija. Zatim ćemo koristiti standardne algoritme heširanja da izračunamo heš običnog teksta u Python-u, koristeći ugrađeni modul `hashlib`.

Za sve ovo i još više, krenimo!

Šta je heširanje?

Proces heširanja uzima ulazni niz (poruku) i daje izlaz fiksne dužine, koji se naziva heš. To znači da je dužina izlaznog heša za dati algoritam heširanja uvek ista, bez obzira na dužinu ulaznog niza. Ali, kako se heširanje razlikuje od enkripcije?

Kod enkripcije, poruka ili običan tekst se šifruju pomoću algoritma za šifrovanje, što daje šifrovani izlaz. Zatim se može primeniti algoritam za dešifrovanje na šifrovani izlaz da bi se dobio originalni niz poruke.

Međutim, heširanje funkcioniše drugačije. Proces enkripcije je inverzibilan, odnosno može se preći sa šifrovane poruke na nešifrovanu i obrnuto.

Za razliku od enkripcije, heširanje nije inverzibilan proces, što znači da ne možemo preći od heša do ulazne poruke.

Svojstva heš funkcija

Pogledajmo neka svojstva koja heš funkcije treba da zadovolje:

  • Determinističnost: Heš funkcije su determinističke. Za datu poruku m, heš od m će uvek biti isti.
  • Otpornost na pre-image napade: Ovo smo već pomenuli kada smo rekli da heširanje nije inverzibilna operacija. Svojstvo otpornosti na pre-image napade znači da je nemoguće dobiti poruku m iz izlaznog heša.
  • Otpornost na kolizije: Trebalo bi da bude teško (ili računarski neizvodljivo) pronaći dva različita niza poruka m1 i m2 tako da heš od m1 bude jednak hešu od m2. Ovo svojstvo se naziva otpornost na kolizije.
  • Otpornost na drugi pre-image: Ovo znači da je, s obzirom na poruku m1 i odgovarajući heš, nemoguće naći drugu poruku m2 tako da je hash(m1) = hash(m2).

Python-ov `hashlib` modul

Python-ov ugrađeni modul `hashlib` pruža implementacije nekoliko algoritama za heširanje i sažetke poruka, uključujući SHA i MD5 algoritme.

Da biste koristili konstruktore i ugrađene funkcije iz Python `hashlib` modula, možete ga uvesti u svoje okruženje na sledeći način:

import hashlib

`hashlib` modul obezbeđuje konstante `algorithms_available` i `algorithms_guaranteed`, koje predstavljaju skup algoritama čije su implementacije dostupne i garantovane na platformi, respektivno.

Prema tome, `algorithms_guaranteed` je podskup od `algorithms_available`.

Pokrenite Python REPL, uvezite `hashlib` i pristupite konstantama `algorithms_available` i `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'}

Možemo videti da je `algorithms_guaranteed` zaista podskup `algorithms_available`.

Kako kreirati heš objekte u Python-u

Hajde da naučimo kako kreirati heš objekte u Python-u. Izračunaćemo SHA256 heš niza poruke koristeći sledeće metode:

  • Generički `new()` konstruktor
  • Konstruktori specifični za algoritam

Korišćenje konstruktora `new()`

Inicijalizujmo niz poruke:

>>> message = "techblog.co.rs is awesome!"

Da bismo instancirali heš objekat, možemo koristiti konstruktor `new()` i proslediti ime algoritma, kao što je prikazano:

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

Sada možemo pozvati metod `update()` na heš objektu sa nizom poruke kao argumentom:

>>> sha256_hash.update(message)

Ako ovo pokušate, dobićete grešku jer algoritmi heširanja mogu da rade samo sa nizovima bajtova.

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

Da biste dobili kodirani niz, možete pozvati metod `encode()` na nizu i koristiti ga u pozivu metoda `update()`. Nakon toga, možete pozvati metod `hexdigest()` da biste dobili SHA256 heš koji odgovara nizu poruke.

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

Umesto da kodirate niz poruke koristeći metod `encode()`, možete ga definisati i kao niz bajtova tako što ćete niz prefiksovati sa `b`, kao što je prikazano:

message = b"techblog.co.rs is awesome!"
sha256_hash.update(message)
sha256_hash.hexdigest()
# Output: 'b360c77de704ad8f02af963d7da9b3bb4e0da6b81fceb4c1b36723e9d6d9de3d'

Dobijeni heš je isti kao prethodni, što potvrđuje determinističku prirodu heš funkcija.

Pored toga, mala promena u nizu poruke bi trebalo da izazove drastičnu promenu heša (poznato i kao „efekat lavine“).

Da bismo to potvrdili, promenimo ‘a’ u ‘awesome’ u ‘A’ i izračunajmo heš:

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

Možemo videti da se heš potpuno menja.

Korišćenje konstruktora specifičnog za algoritam

U prethodnom primeru smo koristili generički konstruktor `new()` i prosledili „SHA256“ kao ime algoritma za kreiranje heš objekta.

Umesto toga, možemo koristiti i `sha256()` konstruktor kao što je prikazano:

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

Izlazni heš je identičan hešu koji smo prethodno dobili za niz poruke „techblog.co.rs is awesome!“.

Istraživanje atributa heš objekata

Heš objekti imaju nekoliko korisnih atributa:

  • Atribut `digest_size` označava veličinu sažetka u bajtovima. Na primer, SHA256 algoritam vraća 256-bitni heš, što je ekvivalentno 32 bajta.
  • Atribut `block_size` se odnosi na veličinu bloka koja se koristi u algoritmu heširanja.
  • Atribut `name` je ime algoritma koje možemo koristiti u konstruktoru `new()`. Pronalaženje vrednosti ovog atributa može biti korisno kada heš objekti nemaju opisna imena.

Možemo proveriti ove atribute za objekat `sha256_hash` koji smo ranije kreirali:

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

Hajde da pogledamo neke zanimljive primene heširanja koristeći Python-ov `hashlib` modul.

Praktični primeri heširanja

Provera integriteta softvera i datoteka

Kao programeri, često preuzimamo i instaliramo softverske pakete. Ovo se odnosi na sve operativne sisteme, bez obzira da li koristite Linux, Windows ili macOS.

Međutim, neki serveri za softverske pakete možda nisu pouzdani. Često možete pronaći heš (ili kontrolni zbir) pored linka za preuzimanje. Možete proveriti integritet preuzetog softvera tako što ćete izračunati heš i uporediti ga sa zvaničnim hešom.

Ovo se takođe može primeniti na datoteke na vašem računaru. Čak i najmanja promena u sadržaju datoteke će drastično promeniti heš, tako da možete proveriti da li je datoteka izmenjena proverom heša.

Evo jednostavnog primera. Napravite tekstualnu datoteku `my_file.txt` u radnom direktorijumu i dodajte joj sadržaj.

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

Zatim možete otvoriti datoteku u binarnom režimu čitanja (`rb`), pročitati sadržaj datoteke i izračunati SHA256 heš, kao što je prikazano:

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

Ovde je promenljiva `original_hash` heš datoteke `my_file.txt` u njenom trenutnom stanju.

>>> original_hash
# Output: '53bfd0551dc06c4515069d1f0dc715d002d451c8799add29f3e5b7328fda9f8f'

Sada izmenite datoteku `my_file.txt`. Možete ukloniti dodatni razmak ispred reči „going“. 🙂

Ponovo izračunajte heš i sačuvajte ga u promenljivoj `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()

Zatim možete dodati jednostavnu `assert` izjavu koja potvrđuje da li je `computed_hash` jednak `original_hash`.

>>> assert computed_hash == original_hash

Ako je datoteka izmenjena (što je tačno u ovom slučaju), trebalo bi da dobijete `AssertionError`:

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

Možete koristiti heširanje kada čuvate osetljive informacije, kao što su lozinke u bazama podataka. Takođe možete koristiti heširanje prilikom autentifikacije lozinke kada se povezujete sa bazama podataka. Potvrdite heš unete lozinke sa hešom ispravne lozinke.

Zaključak

Nadam se da vam je ovaj vodič pomogao da naučite o generisanju sigurnih heševa pomoću Python-a. Evo ključnih zaključaka:

  • Python-ov `hashlib` modul pruža spremne implementacije nekoliko algoritama za heširanje. Možete dobiti listu algoritama garantovanih na vašoj platformi pomoću `hashlib.algorithms_guaranteed`.
  • Da biste kreirali heš objekat, možete koristiti generički konstruktor `new()` sa sintaksom: `hashlib.new(„algoritam-ime“)`. Alternativno, možete koristiti konstruktore koji odgovaraju specifičnim algoritmima heširanja, na primer: `hashlib.sha256()` za SHA256 heš.
  • Nakon inicijalizacije niza poruke koji treba da se hešira i heš objekta, možete pozvati metod `update()` na heš objektu, a zatim metod `hexdigest()` da biste dobili heš.
  • Heširanje može biti korisno kada se proverava integritet softverskih artefakata i datoteka, čuvaju osetljive informacije u bazama podataka i još mnogo toga.

Sledeće, naučite kako da kodirate generator slučajnih lozinki u Python-u.