Challenge 3: Switch to Argon2 — Possible Solution ==================================================================== # settings.py PASSWORD_HASHERS = [ "django.contrib.auth.hashers.Argon2PasswordHasher", "django.contrib.auth.hashers.PBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher", "django.contrib.auth.hashers.BCryptSHA256PasswordHasher", ] # Also requires installing the argon2-cffi package, since Django doesn't # bundle the actual Argon2 implementation itself: # pip install argon2-cffi WHY THE OLD HASHER (PBKDF2) MUST STAY IN THE LIST ------------------------------------------------------ Django only uses the FIRST hasher in PASSWORD_HASHERS to hash NEW passwords going forward (new user registrations, or any password changed via set_password() after this setting takes effect) — but every password hash ALREADY stored in the database was hashed with whatever algorithm was previously configured (PBKDF2, in this case, since that's Django's own default). Every stored password hash is tagged with the algorithm that produced it (Django's hash strings are self-describing, e.g. they start with an identifier like "pbkdf2_sha256$..." or "argon2$..."). When a user logs in, Django looks at their STORED hash's algorithm tag and checks it using whichever hasher in PASSWORD_HASHERS matches that tag — NOT necessarily the first one in the list. If PBKDF2PasswordHasher were removed from the list entirely, every existing user whose password was hashed with PBKDF2 (i.e. everyone who registered before this change) would be UNABLE to log in at all — Django would have no hasher configured that understands their stored hash format, and check_password() would fail for every one of them. Keeping both hashers listed means: - New passwords (new signups, or password changes) get hashed with Argon2id, the stronger, more modern default. - Existing passwords still verify correctly against PBKDF2, so no existing user is locked out. - Django can even be configured to transparently re-hash a user's password with the new algorithm the next time they successfully log in (this is Django's default upgrade behavior), gradually migrating the whole user base to Argon2id over time without a disruptive bulk migration or forced password reset. WHY THIS WORKS -------------- - PASSWORD_HASHERS is an ordered list precisely so Django can prefer a new algorithm for future hashing while still supporting verification against every algorithm ever used historically in this project. - This mirrors the exact scenario the Authentication & Session Security course warned about: security defaults improve over time, and a well-designed system needs to support a gradual, non-disruptive migration path rather than an all-or-nothing cutover.