我正在浏览crackstation.net网站并遇到了这段代码,其评论如下:
在长度 - 恒定时间内比较两个字节数组.使用此比较方法,以便无法使用定时攻击从在线系统中提取密码哈希值,然后离线攻击.
private static bool SlowEquals(byte[] a, byte[] b)
{
uint diff = (uint)a.Length ^ (uint)b.Length;
for (int i = 0; i < a.Length && i < b.Length; i++)
diff |= (uint)(a[i] ^ b[i]);
return diff == 0;
}
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释一下这个函数实际是如何工作的,为什么我们需要将长度转换为无符号整数以及这种方法如何避免定时攻击?这条线diff |= (uint)(a[i] ^ b[i]);做什么?
Jer*_*fin 19
这是diff基于a和之间是否存在差异而设定的b.
它通过始终贯穿的两个较短的整体走避免了定时攻击a和b,不论是否有错配比或没有更早.
该diff |= (uint)(a[i] ^ (uint)b[i])取异或的一个字节的a带的一个相应字节b.如果两个字节相同则为0,如果它们不同则为非零.那or就是diff.
因此,diff如果在该迭代中的输入之间发现差异,则将在迭代中设置为非零.一旦diff在循环的任何迭代中给出非零值,它将通过进一步的迭代保留非零值.
因此,最终的结果在diff如果任何差的对应字节之间发现将是非零a和b,和0仅当的所有字节(和长度)a和b是相等的.
然而,与典型的比较不同,这将始终执行循环,直到两个输入中较短的输入中的所有字节都与另一个中的字节进行比较.一个典型的比较会有一个早期的问题,一旦发现不匹配,循环就会被打破:
bool equal(byte a[], byte b[]) {
if (a.length() != b.length())
return false;
for (int i=0; i<a.length(); i++)
if (a[i] != b[i])
return false;
return true;
}
Run Code Online (Sandbox Code Playgroud)
有了这个,基于返回所消耗的时间量false,我们可以学习(至少近似)在a和之间匹配的字节数b.假设长度的初始测试需要10 ns,并且循环的每次迭代需要另外10 ns.在此基础上,如果在50纳秒返回false,我们可以快速地猜测,我们有正确的长度,和前四个字节a和b匹配.
即使不知道确切的时间量,我们仍然可以使用时序差来确定正确的字符串.我们从一个长度为1的字符串开始,一次增加一个字节,直到我们看到返回false所花费的时间增加.然后我们遍历第一个字节中的所有可能值,直到我们看到另一个增加,表明它已经执行了循环的另一次迭代.对于连续的字节继续相同,直到所有字节匹配并返回true.
原来仍然有一点时间攻击 - 虽然我们不能根据时间轻松确定正确字符串的内容,但我们至少可以根据时间找到字符串长度.因为它只比较两个字符串中较短的字符串,所以我们可以从长度为1,然后是2,然后是3的字符串开始,依此类推,直到时间变得稳定.只要时间增加,我们建议的字符串就比正确的字符串短.当我们给它更长的字符串,但时间保持不变时,我们知道我们的字符串比正确的字符串长.正确的字符串长度将是最短的字符串,需要最长的测试持续时间.
这是否有用取决于具体情况,但无论如何,它显然都会泄露一些信息.为了真正实现最大的安全性,我们可能希望在实际字符串的末尾附加随机垃圾以使其成为用户输入的长度,因此时间与输入的长度成正比,无论它是否更短,相等比正确的字符串更长或更长.
| 归档时间: |
|
| 查看次数: |
2175 次 |
| 最近记录: |