Using hash algorithms, we can verify whether a piece of data is valid by comparing its hash value. For example, to validate a user’s password: we compare the password_md5 stored in the database with the result of md5(password) (calculated from the user’s input). If they match, the password entered by the user is correct.
To prevent hackers from reversing the original password from a hash value using a rainbow table, we must not compute the hash based solely on the raw input. Instead, we need to add a salt to generate different hashes for identical inputs—this significantly increases the difficulty of brute-force cracking for hackers.
If the salt is randomly generated by us, we typically compute the MD5 hash as md5(message + salt). In practice, however, the salt can be treated as a “password”: salting a hash means calculating different hashes for the same message based on different passwords. To verify the hash value, the correct password (salt) must be provided.
This is essentially the Hmac algorithm (Keyed-Hashing for Message Authentication). It uses a standardized algorithm to incorporate a key into the hash calculation process.
Unlike custom salted hash implementations, the Hmac algorithm is universal across all hash functions (e.g., MD5, SHA-1). Replacing custom salt logic with Hmac makes program algorithms more standardized and secure.
Python’s built-in hmac module implements the standard Hmac algorithm. Let’s see how to use Hmac to compute keyed hashes:
First, prepare the raw message to be hashed, a random key, and the hash algorithm (MD5 in this example). The code for using hmac is as follows:
>>> import hmac
>>> message = b'Hello, world!'
>>> key = b'secret'
>>> h = hmac.new(key, message, digestmod='MD5')
>>> # For long messages, call h.update(msg) multiple times
>>> h.hexdigest()
'fa4ee7d173f2d97ee79022d1a7355bcf'
Using Hmac is very similar to standard hash algorithms. The output length of Hmac matches that of the original hash algorithm. Note that both the input key and message must be of type bytes—strings (type str) need to be encoded to bytes first.
Replace the custom salt logic from the previous section with the standard Hmac algorithm to validate user passwords:
import hmac, random
def hmac_md5(key, s):
return hmac.new(key.encode('utf-8'), s.encode('utf-8'), 'MD5').hexdigest()
class User(object):
def __init__(self, username, password):
self.username = username
self.key = ''.join([chr(random.randint(48, 122)) for i in range(20)])
self.password = hmac_md5(self.key, password)
db = {
'michael': User('michael', '123456'),
'bob': User('bob', 'abc999'),
'alice': User('alice', 'alice2008')
}
def login(username, password):
user = db[username]
return user.password == hmac_md5(user.key, password)
# Test cases:
assert login('michael', '123456')
assert login('bob', 'abc999')
assert login('alice', 'alice2008')
assert not login('michael', '1234567')
assert not login('bob', '123456')
assert not login('alice', 'Alice2008')
print('ok')
Python’s built-in hmac module implements the standard Hmac algorithm. It computes a “hashed” digest of a message using a key—Hmac is more secure than standard hash algorithms because different keys generate different hashes for the same message.