Skip to main content

Observable Timing Discrepancy in hmac Module​

📐 PY005
🏷️ observable_timing_discrepancy
ℹ️ CWE-208
⛔️ Error

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).

Fix Iconfix
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)

See also​