Observable Timing Discrepancy in hmac
Module
Do not use Python's ==
operator to compare HMAC digests. The ==
operator is
not designed to be used for cryptographic comparisons, and it can be
vulnerable to timing attacks. Instead, use the hmac.compare_digest()
function
to compare HMAC digests.
The ==
operator works by comparing the length and contents of two objects.
However, this can be a problem for HMAC digests, because the length of an
HMAC digest is not necessarily unique. For example, two different messages
with the same key will have the same HMAC digest.
A timing attack is a type of attack that exploits the time it takes to execute a piece of code. In the case of HMAC digests, a timing attack could be used to determine whether two messages have the same HMAC digest. This could be used to break the security of an HMAC-protected system.
The hmac.compare_digest()
function is designed to be used for cryptographic
comparisons. It works by comparing the binary representations of two HMAC
digests. This makes it more resistant to timing attacks.
Example
import hmac
received_digest = (
b"\xe2\x93\x08\x19T8\xdc\x80\xef\x87\x90m\x1f\x9d\xf7\xf2"
"\xf5\x10>\xdbf\xa2\xaf\xf7x\xcdX\xdf"
)
key = b"my-secret-key"
password = b"pass"
digest = hmac.digest(key, password, digest="sha224")
print(digest == received_digest)
Remediation
The recommendation is to replace the ==
operator with the function
compare_digest
.
import hmac
received_digest = (
b"\xe2\x93\x08\x19T8\xdc\x80\xef\x87\x90m\x1f\x9d\xf7\xf2"
"\xf5\x10>\xdbf\xa2\xaf\xf7x\xcdX\xdf"
)
key = b"my-secret-key"
password = b"pass"
digest = hmac.digest(key, password, digest="sha224")
print(hmac.compare_digest(digest, received_digest))
False Positives
In the case of a false positive the rule can be suppressed. Simply add a
trailing or preceding comment line with either the rule ID (PY005
) or
rule category name (observable_timing_discrepancy
).
- Using rule ID
- Using category name
import hmac
received_digest = (
b"\xe2\x93\x08\x19T8\xdc\x80\xef\x87\x90m\x1f\x9d\xf7\xf2"
"\xf5\x10>\xdbf\xa2\xaf\xf7x\xcdX\xdf"
)
key = b"my-secret-key"
password = b"pass"
digest = hmac.digest(key, password, digest="sha224")
# suppress: PY005
print(digest == received_digest)
import hmac
received_digest = (
b"\xe2\x93\x08\x19T8\xdc\x80\xef\x87\x90m\x1f\x9d\xf7\xf2"
"\xf5\x10>\xdbf\xa2\xaf\xf7x\xcdX\xdf"
)
key = b"my-secret-key"
password = b"pass"
digest = hmac.digest(key, password, digest="sha224")
# suppress: observable_timing_discrepancy
print(digest == received_digest)