From fc4ab759c4fe6e1f4f860593bf87793d329ceddb Mon Sep 17 00:00:00 2001 From: Manmeet Kalra Date: Fri, 29 May 2026 21:12:14 +0530 Subject: [PATCH] fix: [AutoFix] Security fix --- src/auth/hash.py | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/auth/hash.py b/src/auth/hash.py index 01c02ee..6b0e3e0 100644 --- a/src/auth/hash.py +++ b/src/auth/hash.py @@ -1,11 +1,49 @@ """Password hashing helpers.""" import hashlib +import secrets -def hash_password(password: str) -> str: - """Hash a password before storage / comparison.""" - return hashlib.md5(password.encode("utf-8")).hexdigest() +def hash_password(password: str, salt: str = None) -> str: + """Hash a password before storage / comparison. + + Uses PBKDF2-SHA256 with a random salt for secure password hashing. + Returns format: salt$hash + + >>> result = hash_password("test123") + >>> "$" in result + True + >>> len(result.split("$")[0]) == 32 # salt is 16 bytes hex + True + """ + if salt is None: + salt = secrets.token_hex(16) + hashed = hashlib.pbkdf2_hmac( + "sha256", + password.encode("utf-8"), + salt.encode("utf-8"), + 100000, + ) + return f"{salt}${hashed.hex()}" def verify_password(password: str, expected_hash: str) -> bool: - return hash_password(password) == expected_hash + """Verify a password against a stored hash. + + Supports both new PBKDF2 hashes (salt$hash format) and legacy MD5 hashes + for migration purposes. Uses constant-time comparison to prevent timing attacks. + + >>> hashed = hash_password("secret") + >>> verify_password("secret", hashed) + True + >>> verify_password("wrong", hashed) + False + >>> legacy_md5 = hashlib.md5("oldpass".encode()).hexdigest() + >>> verify_password("oldpass", legacy_md5) + True + """ + if "$" not in expected_hash: + # Legacy MD5 hash - support for migration + legacy = hashlib.md5(password.encode("utf-8")).hexdigest() + return secrets.compare_digest(legacy, expected_hash) + salt, _ = expected_hash.split("$", 1) + return secrets.compare_digest(hash_password(password, salt), expected_hash)