NET,避免定时攻击

本文关键字:定时 攻击 NET | 更新日期: 2023-09-27 18:17:03

我在浏览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;
    }

有人能解释一下这个函数的实际工作原理吗?为什么我们需要将长度转换为无符号整数,以及这个方法如何避免定时攻击?线路diff |= (uint)(a[i] ^ b[i]);的作用是什么?

NET,避免定时攻击

根据ab之间是否存在差异来设置diff

它总是遍历ab中较短的一个,无论是否早于此出现失配,都可以避免定时攻击。

CCD_ 7取CCD_ 8的一个字节与对应的CCD_。如果两个字节相同,则为0;如果不同,则为非零。则CCD_ 10与CCD_。

因此,如果在迭代中发现输入之间存在差异,则diff将在迭代中设置为非零。一旦diff在循环的任何迭代中被赋予非零值,它将通过进一步的迭代保持非零值。

因此,如果在ab的相应字节之间发现任何差异,则diff中的最终结果将是非零的,并且仅当ab的所有字节(和长度(相等时,才是0。

然而,与典型的比较不同,这将始终执行循环,直到将两个输入中较短输入中的所有字节与另一个输入中的字节进行比较。一个典型的比较是,一旦发现不匹配,循环就会被破坏:

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;
}

这样,基于返回false所花费的时间量,我们可以学习(至少近似(ab之间匹配的字节数。假设长度的初始测试需要10 ns,循环的每次迭代需要另外10 ns。基于此,如果它在50ns内返回false,我们可以快速猜测我们有合适的长度,并且ab的前四个字节匹配。

即使不知道确切的时间量,我们仍然可以使用时间差来确定正确的字符串。我们从一个长度为1的字符串开始,每次增加一个字节,直到我们看到返回false所需的时间增加。然后,我们遍历第一个字节中的所有可能值,直到我们看到另一个增加,表明它已经执行了循环的另一次迭代。对连续的字节继续执行相同的操作,直到所有字节匹配,我们得到true的返回。

原始版本仍然对定时攻击的位开放——尽管我们不能轻易地根据定时确定正确字符串的内容,但我们至少可以根据定时找到字符串长度。由于它只与两个字符串中较短的字符串进行比较,因此我们可以从长度为1、然后为2、然后为3的字符串开始,以此类推,直到时间变得稳定。只要时间在增加,我们提出的字符串就比正确的字符串短。当我们给它更长的字符串,但时间保持不变时,我们知道我们的字符串比正确的字符串长。正确的字符串长度将是测试所需的最短持续时间。

这是否有用取决于情况,但无论如何,它显然泄露了一些信息。为了获得真正最大的安全性,我们可能希望将随机垃圾附加到实际字符串的末尾,使其成为用户输入的长度,因此时间与输入的长度成比例,无论它是短于、等于还是长于正确的字符串。

此版本适用于输入"a"的长度

    private static bool SlowEquals(byte[] a, byte[] b)
    {
        uint diff = (uint)a.Length ^ (uint)b.Length;
        byte[] c = new byte[] { 0 };
        for (int i = 0; i < a.Length; i++)
            diff |= (uint)(GetElem(a, i, c, 0) ^ GetElem(b, i, c, 0));
        return diff == 0;
    }
    private static byte GetElem(byte[] x, int i, byte[] c, int i0)
    {
        bool ok = (i < x.Length);
        return (ok ? x : c)[ok ? i : i0];
    }