Säker hashing med Python Hashlib

Den här handledningen kommer att lära dig hur du skapar säkra hashar med inbyggd funktionalitet från Pythons hashlib-modul.

Att förstå betydelsen av hash och hur man programmatiskt beräknar säkra hash kan vara till hjälp – även om du inte arbetar med programsäkerhet. Men varför?

Tja, när du arbetar med Python-projekt kommer du sannolikt att stöta på tillfällen där du är orolig för att lagra lösenord och annan känslig information i databaser eller källkodsfiler. I sådana fall är det säkrare att köra hashalgoritmen på känslig information och lagra hashen istället för informationen.

I den här guiden kommer vi att täcka vad hashing är och hur det skiljer sig från kryptering. Vi kommer också att gå över egenskaperna för säkra hashfunktioner. Sedan kommer vi att använda vanliga hashalgoritmer för att beräkna hashen för klartext i Python. För att göra detta använder vi den inbyggda hashlib-modulen.

För allt detta och mer, låt oss komma igång!

Vad är hashing?

Processen med hash tar in en meddelandesträng och ger en utdata med fast längd som kallas hash. Det betyder att längden på utdatahashen för en given hashalgoritm är fixerad – oavsett ingångens längd. Men hur skiljer det sig från kryptering?

Vid kryptering krypteras meddelandet eller vanlig text med en krypteringsalgoritm som ger en krypterad utdata. Vi kan sedan köra dekrypteringsalgoritmen på den krypterade utgången för att få tillbaka meddelandesträngen.

Hashningen fungerar dock annorlunda. Vi har precis lärt oss att krypteringsprocessen är inverterbar genom att du kan gå från det krypterade meddelandet till det okrypterade meddelandet och vice versa.

Till skillnad från kryptering är hashing inte en inverterbar process, vilket innebär att vi inte kan gå från hashen till inmatningsmeddelandet.

Egenskaper för hashfunktioner

Låt oss snabbt gå igenom några egenskaper som hashfunktioner bör uppfylla:

  • Deterministisk: Hashfunktioner är deterministiska. Givet ett meddelande m är hashen för m alltid densamma.
  • Preimage Resistant: Vi har redan täckt detta när vi sa att hashing inte är en inverterbar operation. Preimage motståndsegenskapen anger att det är omöjligt att hitta meddelandet m från utdata-hash.
  • Kollisionsbeständig: Det borde vara svårt (eller beräkningsmässigt omöjligt) att hitta två olika meddelandesträngar m1 och m2 så att hashen för m1 är lika med hashen för m2. Denna egenskap kallas kollisionsmotstånd.
  • Andra Preimage Resistant: Detta betyder att givet ett meddelande m1 och motsvarande hash m2, är det omöjligt att hitta ett annat meddelande m2 så att hash(m1) = hash(m2).

Pythons hashlib-modul

Pythons inbyggda hashlib-modul tillhandahåller implementeringar av flera hash- och meddelandesammanfattningsalgoritmer inklusive SHA- och MD5-algoritmerna.

För att använda konstruktörerna och de inbyggda funktionerna från Python hashlib-modulen kan du importera den till din arbetsmiljö så här:

import hashlib

Hashlib-modulen tillhandahåller konstanterna algorithms_available och algorithms_guaranteed, som anger uppsättningen algoritmer vars implementeringar är tillgängliga respektive garanteras på en plattform.

Därför är algorithms_guaranteed en delmängd av algorithms_available.

Starta en Python REPL, importera hashlib och få tillgång till konstanterna algorithms_available och 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'}

Vi ser att algorithms_guaranteed verkligen är en delmängd av algorithms_available

Hur man skapar Hash-objekt i Python

Låt oss sedan lära oss hur man skapar hash-objekt i Python. Vi kommer att beräkna SHA256-hash för en meddelandesträng med följande metoder:

  • Den generiska new()-konstruktorn
  • Algoritmspecifika konstruktörer

Använder den nya() konstruktorn

Låt oss initiera meddelandesträngen:

>>> message = "adminvista.com is awesome!"

För att instansiera hash-objektet kan vi använda konstruktorn new() och skicka in namnet på algoritmen som visas:

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

Vi kan nu anropa metoden update() på hash-objektet med meddelandesträngen som argument:

>>> sha256_hash.update(message)

Om du gör det kommer du att stöta på ett fel eftersom hashalgoritmer bara kan fungera med bytesträngar.

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

För att få den kodade strängen kan du anropa metoden encode() på metodsträngen och sedan använda den i metodanropet update(). Efter att ha gjort det kan du anropa metoden hexdigest() för att få sha256-hash som motsvarar meddelandesträngen.

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

Istället för att koda meddelandesträngen med metoden encode() kan du också definiera den som en sträng med bytes genom att prefixet strängen med b så här:

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

Den erhållna hashen är densamma som tidigare hash, vilket bekräftar hashfunktionernas deterministiska karaktär.

Dessutom bör en liten ändring i meddelandesträngen göra att hashen ändras drastiskt (även känd som ”lavineffekt”).

För att verifiera detta, låt oss ändra ’a’ i ’awesome’ till ’A’ och beräkna hashen:

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

Vi ser att hashen förändras helt.

Använda den algoritmspecifika konstruktören

I föregående exempel använde vi den generiska new()-konstruktorn och skickade in ”SHA256” som namnet på algoritmen för att skapa hash-objektet.

Istället för att göra det kan vi också använda sha256()-konstruktorn som visas:

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

Utdata-hash är identisk med hash som vi fick tidigare för meddelandesträngen ”adminvista.com is awesome!”.

Utforska attributen för Hash-objekt

Hashobjekten har några användbara attribut:

  • Attributet digest_size anger storleken på sammanfattningen i byte. Till exempel returnerar SHA256-algoritmen en 256-bitars hash, vilket motsvarar 32 byte
  • Attributet block_size hänvisar till blockstorleken som används i hashalgoritmen.
  • Namnattributet är namnet på algoritmen som vi kan använda i new()-konstruktorn. Att slå upp värdet på det här attributet kan vara användbart när hashobjekten inte har beskrivande namn.

Vi kan kontrollera dessa attribut för sha256_hash-objektet som vi skapade tidigare:

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

Låt oss sedan titta på några intressanta tillämpningar för hash med Pythons hashlib-modul.

Praktiska exempel på hashing

Verifiering av programvarans och filernas integritet

Som utvecklare laddar vi ner och installerar mjukvarupaket hela tiden. Detta gäller oavsett om du arbetar på Linux-distro eller på en Windows eller en Mac.

Vissa speglar för programvarupaket kanske inte är pålitliga. Du kan hitta hashen (eller checksumman) bredvid nedladdningslänken. Och du kan verifiera integriteten hos den nedladdade programvaran genom att beräkna hashen och jämföra den med den officiella hashen.

Detta kan också tillämpas på filer på din maskin. Även den minsta förändring i filinnehållet kommer att förändra hashen drastiskt, du kan kontrollera om en fil har modifierats genom att verifiera hashen.

Här är ett enkelt exempel. Skapa en textfil ’my_file.txt’ i arbetskatalogen och lägg till lite innehåll till den.

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

Du kan sedan öppna filen i läs binärt läge (’rb’), läsa in innehållet i filen och beräkna SHA256-hash som visas:

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

Här är variabeln original_hash hashen för ’min_fil.txt’ i dess nuvarande tillstånd.

>>> original_hash
# Output: '53bfd0551dc06c4515069d1f0dc715d002d451c8799add29f3e5b7328fda9f8f'

Ändra nu filen ’my_file.txt’. Du kan ta bort det extra inledande blanktecken före ordet ”pågår”. 🙂

Beräkna hashen igen och lagra den i variabeln 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()

Du kan sedan lägga till en enkel assert-sats som hävdar om computed_hash är lika med original_hash.

>>> assert computed_hash == original_hash

Om filen ändras (vilket är sant i det här fallet) bör du få ett AssertionError:

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

Du kan använda hash när du lagrar känslig information, såsom lösenord i databaser. Du kan också använda hash i lösenordsautentisering när du ansluter till databaser. Validera hashen för det inmatade lösenordet mot hashen för det korrekta lösenordet.

Slutsats

Jag hoppas att den här handledningen hjälpte dig att lära dig hur du genererar säkra hashs med Python. Här är de viktigaste takeaways:

  • Pythons hashlib-modul ger färdiga implementeringar av flera hashalgoritmer. Du kan få listan över garanterade algoritmer på din plattform med hashlib.algorithms_guaranteed.
  • För att skapa ett hash-objekt kan du använda den generiska new()-konstruktorn med syntaxen: hashlib.new(“algo-name”). Alternativt kan du använda konstruktörerna som motsvarar de specifika hashalgoritmerna, som så: hashlib.sha256() för SHA 256-hash.
  • Efter att ha initierat meddelandesträngen som ska hashas och hash-objektet, kan du anropa update()-metoden på hash-objektet, följt av hexdigest()-metoden för att få hash.
  • Hashing kan vara praktiskt när man kontrollerar integriteten hos programvaruartefakter och filer, lagrar känslig information i databaser och mer.

Lär dig sedan hur du kodar en slumpmässig lösenordsgenerator i Python.