Sikker hashing med Python Hashlib

Denne opplæringen vil lære deg hvordan du lager sikre hasher ved hjelp av innebygd funksjonalitet fra Pythons hashlib-modul.

Det kan være nyttig å forstå betydningen av hashing og hvordan du programmatisk beregner sikre hasher – selv om du ikke jobber med applikasjonssikkerhet. Men hvorfor?

Vel, når du jobber med Python-prosjekter, vil du sannsynligvis komme over tilfeller der du er bekymret for å lagre passord og annen sensitiv informasjon i databaser eller kildekodefiler. I slike tilfeller er det tryggere å kjøre hashing-algoritmen på sensitiv informasjon og lagre hashen i stedet for informasjonen.

I denne veiledningen vil vi dekke hva hashing er og hvordan det er forskjellig fra kryptering. Vi vil også gå over egenskapene til sikre hash-funksjoner. Deretter bruker vi vanlige hashing-algoritmer for å beregne hashen til ren tekst i Python. For å gjøre dette bruker vi den innebygde hashlib-modulen.

For alt dette og mer, la oss komme i gang!

Hva er hashing?

Prosessen med hashing tar inn en meldingsstreng og gir en utgang med fast lengde kalt hash. Dette betyr at lengden på utdata-hashen for en gitt hashing-algoritme er fast – uavhengig av lengden på inngangen. Men hvordan er det forskjellig fra kryptering?

Ved kryptering krypteres meldingen eller ren tekst ved hjelp av en krypteringsalgoritme som gir en kryptert utgang. Vi kan deretter kjøre dekrypteringsalgoritmen på den krypterte utgangen for å få tilbake meldingsstrengen.

Imidlertid fungerer hashing annerledes. Vi har nettopp lært at krypteringsprosessen er inverterbar ved at du kan gå fra den krypterte meldingen til den ukrypterte meldingen og omvendt.

I motsetning til kryptering er hashing ikke en inverterbar prosess, noe som betyr at vi ikke kan gå fra hashen til inndatameldingen.

Egenskaper til hasjfunksjoner

La oss raskt gå gjennom noen egenskaper som hash-funksjoner bør tilfredsstille:

  • Deterministisk: Hash-funksjoner er deterministiske. Gitt en melding m, er hashen til m alltid den samme.
  • Preimage Resistant: Vi har allerede dekket dette da vi sa at hashing ikke er en inverterbar operasjon. Preimage-resistensegenskapen sier at det er umulig å finne meldingen m fra utdata-hashen.
  • Kollisjonsbestandig: Det burde være vanskelig (eller beregningsmessig umulig) å finne to forskjellige meldingsstrenger m1 og m2 slik at hashen til m1 er lik hashen til m2. Denne egenskapen kalles kollisjonsmotstand.
  • Second Preimage Resistant: Dette betyr at gitt en melding m1 og den tilsvarende hash m2, er det umulig å finne en annen melding m2 slik at hash(m1) = hash(m2).

Pythons hashlib-modul

Pythons innebygde hashlib-modul gir implementeringer av flere hashing- og meldingssammendrag-algoritmer, inkludert SHA- og MD5-algoritmene.

For å bruke konstruktørene og de innebygde funksjonene fra Python hashlib-modulen, kan du importere den til arbeidsmiljøet ditt slik:

import hashlib

Hashlib-modulen gir konstantene algorithms_available og algorithms_garanteed, som betegner settet med algoritmer hvis implementeringer er tilgjengelige og er garantert på en plattform, henholdsvis.

Derfor er algorithms_guaranteed en undergruppe av algorithms_available.

Start en Python REPL, importer hashlib og få tilgang til konstantene algorithms_available og algorithms_garanteed:

>>> 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 at algorithms_garanteed faktisk er en undergruppe av algorithms_available

Hvordan lage Hash-objekter i Python

La oss deretter lære hvordan du lager hash-objekter i Python. Vi beregner SHA256-hashen til en meldingsstreng ved å bruke følgende metoder:

  • Den generiske new()-konstruktøren
  • Algoritmespesifikke konstruktører

Bruker den nye() konstruktøren

La oss initialisere meldingsstrengen:

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

For å instansiere hash-objektet kan vi bruke new()-konstruktøren og sende inn navnet på algoritmen som vist:

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

Vi kan nå kalle opp update()-metoden på hash-objektet med meldingsstrengen som argument:

>>> sha256_hash.update(message)

Hvis du gjør det, vil du få en feil da hashing-algoritmer bare kan fungere med byte-strenger.

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

For å få den kodede strengen, kan du kalle encode()-metoden på metodestrengen, og deretter bruke den i update()-metodekallet. Etter å ha gjort det, kan du kalle hexdigest()-metoden for å få sha256-hashen som tilsvarer meldingsstrengen.

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

I stedet for å kode meldingsstrengen ved å bruke encode()-metoden, kan du også definere den som en streng med byte ved å sette foran strengen med b slik:

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

Den oppnådde hashen er den samme som tidligere hash, som bekrefter den deterministiske karakteren til hashfunksjoner.

I tillegg bør en liten endring i meldingsstrengen føre til at hashen endres drastisk (også kjent som «skredeffekt»).

For å bekrefte dette, la oss endre «a» i «awesome» til «A», og beregne hashen:

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

Vi ser at hasjen endres fullstendig.

Bruke den algoritmespesifikke konstruktøren

I forrige eksempel brukte vi den generiske new()-konstruktøren og ga «SHA256» som navnet på algoritmen for å lage hash-objektet.

I stedet for å gjøre det, kan vi også bruke sha256()-konstruktøren som vist:

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

Utdata-hashen er identisk med hashen vi fikk tidligere for meldingsstrengen «tipsbilk.net is awesome!».

Utforske attributtene til Hash-objekter

Hash-objektene har noen få nyttige attributter:

  • Digest_size-attributtet angir størrelsen på sammendraget i byte. For eksempel returnerer SHA256-algoritmen en 256-bits hash, som tilsvarer 32 byte
  • Block_size-attributtet refererer til blokkstørrelsen som brukes i hashing-algoritmen.
  • Navnattributtet er navnet på algoritmen som vi kan bruke i new()-konstruktøren. Det kan være nyttig å slå opp verdien av dette attributtet når hash-objektene ikke har beskrivende navn.

Vi kan sjekke disse attributtene for sha256_hash-objektet vi opprettet tidligere:

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

La oss deretter se på noen interessante applikasjoner for hashing ved å bruke Pythons hashlib-modul.

Praktiske eksempler på hashing

Verifiserer integriteten til programvare og filer

Som utviklere laster vi ned og installerer programvarepakker hele tiden. Dette gjelder uansett om du jobber på Linux-distroen eller på en Windows eller en Mac.

Det kan imidlertid hende at enkelte speil for programvarepakker ikke er pålitelige. Du kan finne hashen (eller sjekksummen) ved siden av nedlastingslenken. Og du kan verifisere integriteten til den nedlastede programvaren ved å beregne hashen og sammenligne den med den offisielle hashen.

Dette kan også brukes på filer på maskinen din. Selv den minste endringen i filinnholdet vil endre hashen drastisk, du kan sjekke om en fil har blitt endret ved å verifisere hashen.

Her er et enkelt eksempel. Lag en tekstfil «my_file.txt» i arbeidskatalogen, og legg til noe innhold i 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 deretter åpne filen i les binær modus («rb»), lese inn innholdet i filen og beregne SHA256-hashen som vist:

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

Her er variabelen original_hash hashen til «my_file.txt» i gjeldende tilstand.

>>> original_hash
# Output: '53bfd0551dc06c4515069d1f0dc715d002d451c8799add29f3e5b7328fda9f8f'

Endre nå filen «my_file.txt». Du kan fjerne det ekstra innledende mellomrommet før ordet «pågår». 🙂

Beregn hashen igjen og lagre den i computed_hash-variabelen.

>>> 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 deretter legge til en enkel assert-setning som hevder om computed_hash er lik original_hash.

>>> assert computed_hash == original_hash

Hvis filen er endret (som er sant i dette tilfellet), bør du få en AssertionError:

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

Du kan bruke hashing når du lagrer sensitiv informasjon, for eksempel passord i databaser. Du kan også bruke hashing i passordautentisering når du kobler til databaser. Valider hashen til det angitte passordet mot hashen til riktig passord.

Konklusjon

Jeg håper denne opplæringen hjalp deg med å lære om å generere sikre hasher med Python. Her er de viktigste takeawayene:

  • Pythons hashlib-modul gir klare til bruk implementeringer av flere hashing-algoritmer. Du kan få listen over garanterte algoritmer på plattformen din ved å bruke hashlib.algorithms_guaranteed.
  • For å lage et hash-objekt kan du bruke den generiske new()-konstruktøren med syntaksen: hashlib.new(“algo-name”). Alternativt kan du bruke konstruktørene som tilsvarer de spesifikke hashing-algoritmene, slik: hashlib.sha256() for SHA 256-hashen.
  • Etter å ha initialisert meldingsstrengen som skal hash og hash-objektet, kan du kalle opp update()-metoden på hash-objektet, etterfulgt av hexdigest()-metoden for å få hash.
  • Hashing kan være nyttig når du sjekker integriteten til programvareartefakter og filer, lagrer sensitiv informasjon i databaser og mer.

Deretter lærer du hvordan du koder en tilfeldig passordgenerator i Python.