Ga naar hoofdinhoud

Wachtwoorden: registreren met Argon2

Argon2 uit les 4 doet alles wat je in de lessen hiervoor nodig had: een hash die je niet kunt terugrekenen, een eigen zout per wachtwoord, en expres traag. In je server verandert alleen /registreer.

Argon2 in /registreer​

from argon2 import PasswordHasher
from fastapi import FastAPI, Form
from sqlitedict import SqliteDict

app = FastAPI()
ph = PasswordHasher()

@app.post("/registreer")
async def registreer(naam: str = Form(...), wachtwoord: str = Form(...)):
with SqliteDict("gebruikers.db") as db:
db[naam] = ph.hash(wachtwoord)
db.commit()
return {"bericht": f"Welkom, {naam}"}
  1. Regel 6:

    Eén PasswordHasher voor de hele server, met instellingen die veilig zijn voor nu.

  2. Regel 11:

    ph.hash maakt de hash, met een nieuw zout erin. Die gaat de database in, het wachtwoord zelf niet.

Zet de import en ph = PasswordHasher() bovenaan je main.py, en vervang je /registreer door deze. /inloggen laat je nog even staan: die zet je in les 6 om.

Inloggen werkt tot les 6 niet

/inloggen vergelijkt nog met een sha256-hash, en in de database staan nu Argon2-hashes. Tot je /inloggen in les 6 omzet, lukt inloggen niet, ook niet met het goede wachtwoord.

Gooi gebruikers.db weg: daar staan nog wachtwoorden of sha256-hashes in uit de lessen hiervoor. Start je server en draai registreer.py en lees_database.py uit les 1 opnieuw:

sara: $argon2id$v=19$m=65536,t=3,p=4$X9osXpPM7r/H8fRMyPCE+Q$etV1GLXtmN/ukJB8evD8IraZPm5jt9WONzagikgkl+Y
alex: $argon2id$v=19$m=65536,t=3,p=4$rUMs0YcTR/e1avCX01vrOg$W5Flt1JoAhellSR9YETx3ZUDa6GbtisQrZIkWsRXIVs
noor: $argon2id$v=19$m=65536,t=3,p=4$l6Q0IpvVuX7N4fBZ5PP8nw$sxddUqdV6fZMCPkJwMIlVqfwTAmuvgOQYSZjhAH2H5Q

Jouw codes zijn anders, want het zout is elke keer nieuw. Sara en Noor hebben nog steeds hetzelfde wachtwoord, maar dat zie je nergens meer aan.

Wat er in de hash staat​

Een Argon2-hash bestaat uit stukken, gescheiden door een $:

StukBetekent
argon2idwelk algoritme
m=65536,t=3,p=4hoeveel geheugen en rekenwerk het kost
X9osXpPM7r/H8fRMyPCE+Qhet zout
etV1GLXt…de hash zelf

Het zout staat er dus in, zoals les 3 al zei: het hoeft niet geheim te zijn. En omdat ook de instellingen erin staan, heb je later bij het inloggen aan de opgeslagen code genoeg.

Opdrachten​

Opdracht 1: Predict - Twee keer hashen​

from argon2 import PasswordHasher

ph = PasswordHasher()
print(ph.hash("welkom123") == ph.hash("welkom123"))

Vraag: print dit True of False?

Tip

Wat doet ph.hash elke keer anders dan sha256?

Antwoord

False. Elke ph.hash krijgt een nieuw zout, dus twee hashes van hetzelfde wachtwoord verschillen. Hoe je dan controleert of een wachtwoord klopt, zie je in les 6.

Opdracht 2: Run​

Zet Argon2 in je /registreer, gooi gebruikers.db weg, en draai registreer.py en lees_database.py. Begint elke code met $argon2id$, en verschillen de codes van Sara en Noor?

Antwoord

Ja: elke code begint met iets als $argon2id$v=19$m=65536,t=3,p=4$. De getallen na m=, t= en p= kunnen bij jou anders zijn, want een nieuwere versie van argon2-cffi kan strengere instellingen hebben. Daarna verschillen de codes allemaal, ook die van Sara en Noor. Dat komt door het zout.

Zo ziet je main.py er nu uit
import hashlib

from argon2 import PasswordHasher
from fastapi import FastAPI, Form
from sqlitedict import SqliteDict

app = FastAPI()
ph = PasswordHasher()

@app.post("/registreer")
async def registreer(naam: str = Form(...), wachtwoord: str = Form(...)):
with SqliteDict("gebruikers.db") as db:
db[naam] = ph.hash(wachtwoord)
db.commit()
return {"bericht": f"Welkom, {naam}"}

@app.post("/inloggen")
async def inloggen(naam: str = Form(...), wachtwoord: str = Form(...)):
with SqliteDict("gebruikers.db") as db:
if db.get(naam) == hashlib.sha256(wachtwoord.encode()).hexdigest():
return {"bericht": "Ingelogd"}
return {"bericht": "Naam of wachtwoord klopt niet"}

/inloggen rekent hier nog met sha256; dat verandert in les 6.

Door naar les 6: inloggen met verify.