Skip to content

hmac — timing attack

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

hmac_timing_attack.py
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-super-duper-secret-key-string"
password = b"pass"
digest = hmac.digest(key, password, digest="sha224")

print(digest == received_digest)
Example Output
> precli tests/unit/rules/python/stdlib/hmac/examples/hmac_timing_attack.py
⛔️ Error on line 13 in tests/unit/rules/python/stdlib/hmac/examples/hmac_timing_attack.py
PY005: Observable Timing Discrepancy
Comparing digests with the '==' operator is vulnerable to timing attacks.

Remediation

The recommendation is to replace the == operator with the function compare_digest.

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

Default Configuration

enabled = true
level = "error"

See also

New in version 0.1.4