Hashing securizat cu Python Hashlib

Acest tutorial vă va învăța cum să creați hashuri sigure folosind funcționalitatea încorporată din modulul hashlib al lui Python.

Înțelegerea importanței hashing-ului și a modului de calculare programatică a hashurilor securizate poate fi utilă, chiar dacă nu lucrați în securitatea aplicației. Dar de ce?

Ei bine, atunci când lucrați la proiecte Python, probabil veți întâlni situații în care vă îngrijorează stocarea parolelor și a altor informații sensibile în baze de date sau fișiere de cod sursă. În astfel de cazuri, este mai sigur să rulați algoritmul de hashing pe informații sensibile și să stocați hash-ul în loc de informații.

În acest ghid, vom acoperi ce este hashingul și cum este diferit de criptare. Vom trece și peste proprietățile funcțiilor hash securizate. Apoi, vom folosi algoritmi de hashing obișnuiți pentru a calcula hash-ul textului simplu în Python. Pentru a face acest lucru, vom folosi modulul hashlib încorporat.

Pentru toate acestea și multe altele, să începem!

Ce este hashingul?

Procesul de hashing preia un șir de mesaj și oferă o ieșire cu lungime fixă ​​numită hash. Înseamnă că lungimea hash-ului de ieșire pentru un anumit algoritm de hashing este fixă ​​– indiferent de lungimea intrării. Dar cu ce este diferit de criptare?

În criptare, mesajul sau textul simplu este criptat folosind un algoritm de criptare care oferă o ieșire criptată. Apoi putem rula algoritmul de decriptare pe ieșirea criptată pentru a obține înapoi șirul de mesaj.

Cu toate acestea, hashingul funcționează diferit. Tocmai am aflat că procesul de criptare este inversabil prin faptul că puteți trece de la mesajul criptat la mesajul necriptat și invers.

Spre deosebire de criptare, hashingul nu este un proces inversabil, ceea ce înseamnă că nu putem trece de la hash la mesajul de intrare.

Proprietățile funcțiilor hash

Să trecem rapid peste câteva proprietăți pe care funcțiile hash ar trebui să le satisfacă:

  • Deterministe: funcțiile hash sunt deterministe. Având în vedere un mesaj m, hash-ul lui m este întotdeauna același.
  • Rezistent la imagine: Am tratat deja acest lucru când am spus că hashingul nu este o operație inversabilă. Proprietatea de rezistență preimagine afirmă că este imposibil să găsiți mesajul m din hash-ul de ieșire.
  • Rezistent la coliziune: Ar trebui să fie dificil (sau imposibil din punct de vedere computațional) să găsiți două șiruri de mesaje diferite m1 și m2, astfel încât hash-ul lui m1 să fie egal cu hash-ul lui m2. Această proprietate se numește rezistență la coliziune.
  • Rezistent la al doilea preimagine: asta înseamnă că având în vedere un mesaj m1 și hash-ul corespunzător m2, este imposibil să găsești un alt mesaj m2 astfel încât hash(m1) = hash(m2).
  7 servere web open source pentru site-uri mici până la mari

Modulul hashlib al lui Python

Modulul hashlib încorporat al lui Python oferă implementări ale mai multor algoritmi de hashing și de rezumare a mesajelor, inclusiv algoritmii SHA și MD5.

Pentru a utiliza constructorii și funcțiile încorporate din modulul Python hashlib, îl puteți importa în mediul dvs. de lucru astfel:

import hashlib

Modulul hashlib furnizează constantele algorithms_available și algorithms_guaranteed, care denotă setul de algoritmi ale căror implementări sunt disponibile și, respectiv, garantate pe o platformă.

Prin urmare, algorithms_guaranteed este un subset de algorithms_available.

Porniți un REPL Python, importați hashlib și accesați constantele 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'}

Vedem că algorithms_guaranteed este într-adevăr un subset de algorithms_available

Cum se creează obiecte Hash în Python

În continuare, să învățăm cum să creăm obiecte hash în Python. Vom calcula hash-ul SHA256 al unui șir de mesaj folosind următoarele metode:

  • Constructorul generic new().
  • Constructori specifici algoritmului

Folosind constructorul new().

Să inițializam șirul de mesaje:

>>> message = "tipstrick.ro is awesome!"

Pentru a instanția obiectul hash, putem folosi constructorul new() și trecem numele algoritmului așa cum se arată:

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

Acum putem apela metoda update() pe obiectul hash cu șirul de mesaj ca argument:

>>> sha256_hash.update(message)

Dacă faceți acest lucru, veți întâlni o eroare, deoarece algoritmii de hashing pot funcționa numai cu șiruri de octeți.

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

Pentru a obține șirul codificat, puteți apela metoda encode() pe șirul metodei și apoi o puteți utiliza în apelul metodei update(). După ce faceți acest lucru, puteți apela metoda hexdigest() pentru a obține hash-ul sha256 corespunzător șirului de mesaj.

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

În loc să codificați șirul de mesaj folosind metoda encode(), îl puteți defini și ca șir de octeți, prefixând șirul cu b astfel:

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

Hash-ul obținut este același cu hash-ul anterior, ceea ce confirmă natura deterministă a funcțiilor hash.

  6 motive pentru care ar trebui să utilizați Google Foto pe iPhone

În plus, o mică modificare a șirului de mesaje ar trebui să facă ca hash-ul să se schimbe drastic (cunoscut și ca „efect de avalanșă”).

Pentru a verifica acest lucru, să schimbăm „a” din „awesome” cu „A” și să calculăm hash:

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

Vedem că hash-ul se schimbă complet.

Folosind constructorul specific algoritmului

În exemplul anterior, am folosit constructorul generic new() și am trecut în „SHA256” ca nume al algoritmului pentru a crea obiectul hash.

În loc să facem acest lucru, putem folosi și constructorul sha256() așa cum se arată:

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

Hash-ul de ieșire este identic cu hash-ul pe care l-am obținut mai devreme pentru șirul de mesaj „tipstrick.ro is awesome!”.

Explorarea atributelor obiectelor Hash

Obiectele hash au câteva atribute utile:

  • Atributul digest_size denotă dimensiunea digest-ului în octeți. De exemplu, algoritmul SHA256 returnează un hash de 256 de biți, care este echivalent cu 32 de octeți
  • Atributul block_size se referă la dimensiunea blocului utilizată în algoritmul de hashing.
  • Atributul name este numele algoritmului pe care îl putem folosi în constructorul new(). Căutarea valorii acestui atribut poate fi utilă atunci când obiectele hash nu au nume descriptive.

Putem verifica aceste atribute pentru obiectul sha256_hash pe care l-am creat mai devreme:

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

În continuare, să ne uităm la câteva aplicații interesante de hashing folosind modulul hashlib al lui Python.

Exemple practice de hashing

Verificarea integrității software-ului și fișierelor

În calitate de dezvoltatori, descarcăm și instalăm pachete software tot timpul. Acest lucru este adevărat indiferent dacă lucrați pe distribuția Linux sau pe un Windows sau un Mac.

Cu toate acestea, unele oglinzi pentru pachetele software pot să nu fie de încredere. Puteți găsi hash-ul (sau suma de control) lângă linkul de descărcare. Și puteți verifica integritatea software-ului descărcat calculând hash-ul și comparându-l cu hash-ul oficial.

  Cum să calculați și să reduceți rata mare de uzură a angajaților

Acest lucru poate fi aplicat și fișierelor de pe computer. Chiar și cea mai mică modificare a conținutului fișierului va schimba hash-ul drastic, puteți verifica dacă un fișier a fost modificat verificând hash-ul.

Iată un exemplu simplu. Creați un fișier text „my_file.txt” în directorul de lucru și adăugați conținut la acesta.

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

Apoi puteți deschide fișierul în modul de citire binar („rb”), puteți citi conținutul fișierului și puteți calcula hash-ul SHA256 așa cum se arată:

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

Aici, variabila original_hash este hash-ul lui „my_file.txt” în starea sa curentă.

>>> original_hash
# Output: '53bfd0551dc06c4515069d1f0dc715d002d451c8799add29f3e5b7328fda9f8f'

Acum modificați fișierul „my_file.txt”. Puteți elimina spațiul alb suplimentar înainte de cuvântul „going”. 🙂

Calculați din nou hash-ul și stocați-l în variabila 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()

Puteți adăuga apoi o instrucțiune assert simplă care afirmă dacă computed_hash este egal cu original_hash.

>>> assert computed_hash == original_hash

Dacă fișierul este modificat (ceea ce este adevărat în acest caz), ar trebui să obțineți o AssertionError:

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

Puteți utiliza hashing atunci când stocați informații sensibile, cum ar fi parolele în bazele de date. De asemenea, puteți utiliza hashing în autentificarea parolei atunci când vă conectați la bazele de date. Validați hash-ul parolei introduse față de hash-ul parolei corecte.

Concluzie

Sper că acest tutorial v-a ajutat să învățați despre generarea hashurilor securizate cu Python. Iată principalele concluzii:

  • Modulul hashlib al lui Python oferă implementări gata de utilizare a mai multor algoritmi de hashing. Puteți obține lista de algoritmi garantați pe platforma dvs. folosind hashlib.algorithms_guaranteed.
  • Pentru a crea un obiect hash, puteți folosi constructorul generic new() cu sintaxa: hashlib.new(„nume algor”). Alternativ, puteți utiliza constructorii corespunzători algoritmilor de hashing specifici, astfel: hashlib.sha256() pentru hash-ul SHA 256.
  • După inițializarea șirului de mesaje care urmează să fie hash și a obiectului hash, puteți apela metoda update() pe obiectul hash, urmată de metoda hexdigest() pentru a obține hash-ul.
  • Hashingul poate fi util atunci când se verifică integritatea artefactelor și fișierelor software, se stochează informații sensibile în baze de date și multe altele.

Apoi, aflați cum să codificați un generator de parole aleatorii în Python.