Python 中的 HMAC 比较怎么可能不是恒定时间的?

How could HMAC comparison ever not be constant-time in Python?

Python有个专门比较HMAC防止定时攻击的方法:https://docs.python.org/3.7/library/hmac.html#hmac.compare_digest

我在这里读到了定时攻击:https://security.stackexchange.com/questions/74547/timing-attack-against-hmac-in-authenticated-encryption

我的问题是,它怎么可能不是恒定时间?为了比较它,有必要计算实际的 HMAC,而且你不可能一次计算摘要 1 个字符,对吧?最后,它只是一个简单的字符串比较,比我测试中的实际 HMAC 计算快 2 个数量级。那么这里的攻击面到底在哪里呢?如果我不使用 hmac.compare_digest(),有人可以举例说明实际漏洞的确切位置吗?

At the end, it would just be a simple string comparison, which is 2 orders of magnitude faster than the actual HMAC calculation in my tests.

但是不是常数时间。仅仅因为它们完成得很快并不意味着差异是无法衡量的。对于 bytes 值,Python 在使用 memcmp 测试其余部分之前首先测试相等长度和相等的第一个字节。对于字符串,Python比较长度,然后种类(如果字符串每个字符使用1、2或4个字节),然后还使用memcmp.

memcmp 的 Linux 联机帮助页明确指出:

Do not use memcmp() to compare security critical data, such as cryptographic secrets, because the required CPU time depends on the number of equal bytes. Instead, a function that performs comparisons in constant time is required. Some operating systems provide such a function (e.g., NetBSD's consttime_memequal()), but no such function is specified in POSIX. On Linux, it may be necessary to implement such a function oneself.)

足够坚定的攻击者可以利用这个弱点来找出你存储的哈希值与它发送的数据的哈希值。

定时攻击可以伪造签名。比方说,服务将授权信​​息存储在与客户端共享的令牌中。如果客户可以更改此令牌,则他们可以获得他们本来不会拥有的访问权限。为了防止这种情况,使用 HMAC 签名对令牌进行签名,让服务器在接受返回的令牌有效之前验证它。如果授权数据与签名不匹配,令牌将被拒绝。

如果服务器这样做:

auth_data, signature = split_token(token)
expected = hmac_signature(auth_data)
if signature == expected:
    # ...

然后攻击者可以检测到伪造签名中有多少字符与预期签名匹配,并相应地进行调整。他们从 XXXXX:000000... 开始,然后尝试 XXXXX:1000000...,等等,直到服务所用的时间增加,表明他们有一个匹配的第一个字符。然后可以修改第二个字符,直到完整签名匹配。