How can I disable concatenation when using hashlib's update method?

246 Views Asked by At

I've written a method for hashing passwords by using hashlib. I allow the user to send a password through a POST method, which is received by Flask and the password is subsequently hashed so that the hash can be checked against a stored var to see if it's the same.

It works great both when a correct or an incorrect password is sent for the first time. But if the user sends a wrong password on the first POST and then tries again with the correct password, it fails. (One could also think of it as failing if the first try was a success and the user keeps on trying, but I don't care about that for now.)

I was able to narrow the problem down to hashlibs update function

hash.update(arg) Update the hash object with the string arg. Repeated calls are equivalent to a single call with the concatenation of all the arguments: m.update(a); m.update(b) is equivalent to m.update(a+b).

I wanted to know how can I disable the concatenation upon repeated calls. It doesn't matter if it's a hacky workaround.

Here's my code in case it's useful:

h = hashlib.sha256()
VALID_USERNAME = 'admin'
VALID_PASSW_HASH = "210ce034be6d826a451a4261d70494148c5d7101627335ccacf8e00a711bcc5d"

@app.route('/api/queue/auth', methods=['POST'])
def auth():
    username = request.json.get('username')
    password = request.json.get('password')
    if bool(username) is False or bool(password) is False:
        return "\nPlease fill in both fields.\n", 400
    passwordBytes = password.encode(encoding='UTF-8',errors='strict')
    h.update(passwordBytes)
    if h.hexdigest() != VALID_PASSW_HASH or username != VALID_USERNAME:
        return "\nPlease check your username and password, and try again.\n", 401
    r.set('auth', 'true')
    return "Access Granted.\n", 200

Additional notes:

  • the "r.set" line (above the last one) is just because it later does something with Redis.
  • i've checked that passwordBytes always returns the same encoding when the same password is provided (it's deterministic)
  • i've also checked that h.hexdigest() is returning something different if the same password is provided on the first try or on another try. So taking into account these two points, we can be sure that the problem lies in h.update(), probably because of the concatenation feature.
1

There are 1 best solutions below

0
Moshe perez On BEST ANSWER

Just move the first line out of the global scope into the auth() function:

VALID_USERNAME = 'admin'
VALID_PASSW_HASH = "210ce034be6d826a451a4261d70494148c5d7101627335ccacf8e00a711bcc5d"

@app.route('/api/queue/auth', methods=['POST'])
def auth():
    username = request.json.get('username')
    password = request.json.get('password')
    if bool(username) is False or bool(password) is False:
        return "\nPlease fill in both fields.\n", 400
    passwordBytes = password.encode(encoding='UTF-8',errors='strict')
    h = hashlib.sha256()
    h.update(passwordBytes)
    if h.hexdigest() != VALID_PASSW_HASH or username != VALID_USERNAME:
        return "\nPlease check your username and password, and try again.\n", 401
    r.set('auth', 'true')
    return "Access Granted.\n", 200

or even better, refactor the hashing of the password to a different function:

VALID_USERNAME = 'admin'
VALID_PASSW_HASH = "210ce034be6d826a451a4261d70494148c5d7101627335ccacf8e00a711bcc5d"

def hash_password(password):
    passwordBytes = password.encode(encoding='UTF-8',errors='strict')
    h = hashlib.sha256()
    h.update(passwordBytes)
    return h.hexdigest()


@app.route('/api/queue/auth', methods=['POST'])
def auth():
    username = request.json.get('username')
    password = request.json.get('password')
    if bool(username) is False or bool(password) is False:
        return "\nPlease fill in both fields.\n", 400
    if hash_password(password) != VALID_PASSW_HASH or username != VALID_USERNAME:
        return "\nPlease check your username and password, and try again.\n", 401
    r.set('auth', 'true')
    return "Access Granted.\n", 200