From ca08234a810d0f314757b547b6fc200e24307cd5 Mon Sep 17 00:00:00 2001 From: David Sinquin Date: Sat, 4 Aug 2018 22:52:59 +0200 Subject: [PATCH] login handler: Use constant-time comparaison for hashes. An attacker knowing the salt but not the hash could try timming-attacks to guess a password hash and then try to find it from the hash. Although not a high risk, there is no good reason not to use a constant-time comparison, hence this commit. --- re2o/login.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/re2o/login.py b/re2o/login.py index f80f3e42..0bf9aed8 100644 --- a/re2o/login.py +++ b/re2o/login.py @@ -35,6 +35,7 @@ import os from base64 import encodestring, decodestring, b64encode, b64decode from collections import OrderedDict from django.contrib.auth import hashers +from hmac import compare_digest as constant_time_compare ALGO_NAME = "{SSHA}" @@ -63,12 +64,7 @@ def checkPassword(challenge_password, password): salt = challenge_bytes[DIGEST_LEN:] hr = hashlib.sha1(password.encode()) hr.update(salt) - valid_password = True - # La comparaison est volontairement en temps constant - # (pour éviter les timing-attacks) - for i, j in zip(digest, hr.digest()): - valid_password &= i == j - return valid_password + return constant_time_compare(digest, hr.digest()) def hash_password_salt(hashed_password): @@ -118,7 +114,8 @@ class CryptPasswordHasher(hashers.BasePasswordHasher): """ assert encoded.startswith(self.algorithm) salt = hash_password_salt(challenge_password) - return crypt.crypt(password.encode(), salt) == challenge.encode() + return constant_time_compare(crypt.crypt(password.encode(), salt), + challenge.encode()) def safe_summary(self, encoded): """ @@ -159,7 +156,9 @@ class MD5PasswordHasher(hashers.BasePasswordHasher): """ assert encoded.startswith(self.algorithm) salt = hash_password_salt(encoded) - return b64encode(hashlib.md5(password.encode() + salt).digest() + salt) == encoded.encode() + return constant_time_compare( + b64encode(hashlib.md5(password.encode() + salt).digest() + salt), + encoded.encode()) def safe_summary(self, encoded): """