XOR 比较解决的安全问题称为定时攻击。 ...您可以在此处观察比较函数需要多长时间才能成功|失败,并使用该知识来获得优于系统的优势。
有 95 个可打印的 ASCII 字符。如果你有一个 8 个字符的密码,则有 95^8 (6,634,204,312,890,625) 种可能的组合 ...如果正确的密码是你列表中的最后一个,并且你可以每秒尝试 10 亿个密码,大约需要 77 天 暴力破解密码...太长了 - 所以我们需要一个快捷方式!
存储字符串的方法有无数种——可能有十几种流行使用{长度前缀、空终止、...}{Unicode、UTF-8、ASCII、...}。对于这个工作示例,我将使用普遍存在的“使用 ASCII 编码的以 NUL 结尾的字节数组”...IE。 "ABC" 将被存储为 "ABC"NUL 或 {65, 66, 67, 0} ...但是无论您使用什么存储/编码标准,问题本质上都是一样的。
从句法上讲,比较两个字符串的方法和语言一样多,例如。 if str1 == str2 或 if (strcmp(str1, str2) == 0) 等...但是当您查看它们内部的工作方式时,它们几乎都相同。下面是一些简单(但真实)的伪代码,用于执行经典(非安全)字符串比较:
index = 0
LOOP FOREVER {
IF ( (str1[index] == 0) AND (str2[index] == 0) ) THEN return 'same'
IF (str1[index] != str2[index]) THEN return 'different'
index = index + 1
}
假设秘密密码是"BY3"NUL ...让我们尝试一些密码,并注意比较函数必须执行多少操作才能确定成功|失败。
1. "A"NUL ... returns 'different' when 1st char is checked (A) [zero chars are correct]
2. "B"NUL ... returns 'different' when 2nd char is checked (NUL) [first char must be correct]
3. "BX"NUL ... returns 'different' when 2nd char is checked (X) [first char must be correct]
4. "BY"NUL ... returns 'different' when 3rd char is checked (NUL) [first two chars must be correct]
5. "BY1"NUL ... returns 'different' when 3rd char is checked (1) [first two chars must be correct]
6. "BY2"NUL ... returns 'different' when 3rd char is checked (2) [first two chars must be correct]
7. "BY3"NUL ... returns 'same' when the 4th character is checked (NUL) [all three chars are correct]
您可以看到猜测 1 在循环中第一次失败,猜测 2 和 3 在循环中第二次失败......猜测 4、5、6 在循环中第三次失败......并且猜测 7在循环中第四次成功。
通过观察比较函数失败的时间,我们可以判断哪个字符是错误的!这意味着我们实际上可以一次猜出密码一个字符。
再次假设由 95 个可打印字符组成的 8 个字符的密码,我们最后的猜测是正确的......因为我们现在可以一次猜测一个字符,所以需要 95*8 (760 ) 猜测。在每秒 10 亿次猜测中,找到密码大约需要 0.7 毫秒 [闪烁大约需要 100 毫秒] ...这在 77 天内是一个显着的优势 ... 20 个字符密码的优势(95^20 vs 95 * 20)。
那么我们如何阻止攻击者使用定时攻击呢? [剧透:异或]
我们需要做的第一件事是使两个字符串的长度相同;其次,在返回“相同”或“不同”之前,我们必须始终检查每个字符……如果不引入新的计时攻击,这很难做到。但是,与其向您展示许多错误的方法,不如让我们看看正确的方法。
密码应该(在可能的情况下)存储为哈希 ...{DES, MD5, SHA-1, ...} 现在已被证明存在密码缺陷,{SHA-256, SHA-3, Whirlpool, . ..} 仍然很受欢迎 [Oct 2021] ...您可能知道所有哈希(由给定算法生成)的长度相同...因此,如果我们对猜测进行哈希处理并将猜测哈希与存储的哈希进行比较-哈希,我们已经解决了第一个问题 - 我们需要比较的“字符串”(字节数组)现在总是相同的长度。
其次。如何确保我们的比较函数总是花费相同的时间来做出决定......可能有很多方法可以做到这一点,但最常见的解决方案是像这样使用 XOR:
result = 0
index = 0
LOOP WHILE (index < hashLength) {
result = result OR ( secretHash[index] XOR guessHash[index] )
index = index + 1
}
IF result == 0 THEN return 'same' ELSE return 'different'
这样,所有对比较函数的调用都需要相同的时间来运行......不再有计时攻击!
脚注:
对于不熟悉布尔逻辑的读者 - 去阅读;但这里的本质是:
If A and B are the same, (A XOR B) gives a result of 0
If A and B are different, (A XOR B) gives a non-0 result
If A and B are both 0, (A OR B) gives a result of 0
If either A or B are non-0, (A OR B) gives a non-0 result
所以(查看第二个代码块)第一次异或返回非 0(不同)时,结果变为非 0(不同)并且永远不能返回 0(相同)。
搜索“cve 定时攻击”将为您提供现实生活中的示例列表。