西安网络营销学习网站,安徽省建设监理协会,wordpress简洁淘宝客免费主题,市场营销一般在哪上班计时攻击 在计算机安全中#xff0c;计时攻击#xff08;Timing attack#xff09;是旁道攻击 #xff08;Side-channel attack#xff09; 的一种#xff0c;而旁道攻击是根据计算机处理过程发出的信息进行分析#xff0c;包括耗时#xff0c;声音#xff0c;功耗等… 计时攻击 在计算机安全中计时攻击Timing attack是旁道攻击 Side-channel attack 的一种而旁道攻击是根据计算机处理过程发出的信息进行分析包括耗时声音功耗等等这和一般的暴力破解或者利用加密算法本身的弱点进行攻击是不一样的。 举个例子 假如您有一个后端 webapi GetConfig 接口用来获取配置信息调用时需要在 Header 中传入一个秘钥然后判断是否正确并进行返回如下X-Api-Key: x123[HttpGet]
public IActionResult GetConfig()
{var key Request.Headers[X-Api-Key].FirstOrDefault();if (key ! x123){return Unauthorized();}return Ok(configuration);
}注意这里我们为了判断两个字符串相等通常会使用 或者 ! , 实际上背后使用了 String 的 Equals() 方法如下// Determines whether two Strings match.
public static bool Equals(string? a, string? b)
{if (object.ReferenceEquals(a, b)){return true;}if (a is null || b is null || a.Length ! b.Length){return false;}return EqualsHelper(a, b);
}而内部又使用了 SequenceEqual() 方法[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool EqualsHelper(string strA, string strB)
{Debug.Assert(strA ! null);Debug.Assert(strB ! null);Debug.Assert(strA.Length strB.Length);return SpanHelpers.SequenceEqual(ref Unsafe.Aschar, byte(ref strA.GetRawStringData()),ref Unsafe.Aschar, byte(ref strB.GetRawStringData()),((uint)strA.Length) * sizeof(char));
}大概的逻辑是先判断两个字符串长度是否一致如果不是直接返回 false然后循环字符串进行逐位对比一旦发现不相同直接返回 false伪代码如下public bool Equals(string str1, string str2)
{if (str1.Length ! str2.Length) {return false;} for (var i 0; i str1.Length; i){if (str1[i] ! str2[i]){return false;}} return true;
}这里有一个问题是如果字符串第一位不相同直接就返回 false如果最后一位不相同那就需要遍历到最后然后返回 false。不一样的字符串计算的时长可能不一致。 尝试破解 假如用户知道了我们的秘钥的固定长度是 4 位。GET /GetConfig
X-Api-Keya000
Cost: 2ns本次耗时了 2ns, 接下来又输入 b000, c000....GET /GetConfig
X-Api-Keyb000
Cost: 2nsGET /GetConfig
X-Api-Keyc000
Cost: 2ns ...GET /GetConfig
X-Api-Keyx000
Cost: 4ns直到输入了 x000, 发现其他的耗时都是 2ns, 而这里是 4ns大概率判定第一位是 x。注意这里的测试进行了放大可能每个 case 分别调用了 100 次然后统计了 P50中位数得出的结果。然后用同样的方法测试第二位第三位....., 最终破解拿到了秘钥。 使用固定时间的算法 虽然看上去有点扯但确实是真实存在的包括大名鼎鼎的针对 TLS 的 Lucky 13 攻击有兴趣的同学可以看一下。在安全性要求比较高的场景中确实要考虑到计时攻击当涉及到安全时还是宁可信其有。所以我们的算法的执行耗时应该是固定的不应该在不匹配时就立即返回我们尝试改造一下代码public bool Equals(string str1, string str2)
{if (str1.Length ! str2.Length){return false;}bool reult true;for (var i 0; i str1.Length; i){if (str1[i] ! str2[i]){reult false;}}return reult;
}不管怎么样都会遍历完整个字符串然后返回结果看上去没什么问题时间总是固定的但在现代的 CPU 和 .NET 上却不是的因为我们要考虑到分支预测特别是 if 条件。好吧那我们调整一下代码public bool Equals(string str1, string str2)
{if (str1.Length ! str2.Length){return false;}bool reult true;for (var i 0; i str1.Length; i){ reult str1[i] str2[i]; }return reult;
}我们用了运算符 来代替 If, 只有全部为 true 时才会返回 true其中任意一个字符不匹配就会返回 false看上去不错。但是还有一些问题对于bool类型的 result true/false, 我们的 .NET JIT 和 x86 指令执行仍然会进行一些优化我们再调整一下代码public bool Equals(string str1, string str2)
{if (str1.Length ! str2.Length){return false;}int reult 0;for (var i 0; i str1.Length; i){ reult | str1[i] ^ str2[i]; }return reult 0;
}我们把 bool 改成了 int 类型然后使用了运算符 ^ 和 |同样的只有字符串全部匹配时result 为 0,才会返回 true, 其中任意一个不匹配result 就不为 0会返回 false。最后为了防止 JIT 对我们的代码进行其他的优化我们可以加一个特性告诉 JIT 不要管它就像这样[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public bool Equals(string str1, string str2)
{if (str1.Length ! str2.Length){return false;}int reult 0;for (var i 0; i str1.Length; i){ reult | str1[i] ^ str2[i]; }return reult 0;
}上面我们实现了一个针对字符串比较的固定时间的算法来应对计时攻击。实际上 从 .NET Core 2.1 开始就已经做了内置支持我们可以直接使用 FixedTimeEquals 方法, 看一下它的实现[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static bool FixedTimeEquals(ReadOnlySpanbyte left, ReadOnlySpanbyte right)
{ if (left.Length ! right.Length){return false;}int length left.Length;int accum 0;for (int i 0; i length; i){accum | left[i] - right[i];}return accum 0;
}现在用起来也很方便var result CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(str1), Encoding.UTF8.GetBytes(str2)
); 总结 在安全性比较高的场景中应该要考虑到计时攻击可以使用固定时间的算法来应对。在其他的开发语言中也都有本文中类似的算法而在 .NET 中现在我们可以直接使用 CryptographicOperations.FixedTimeEquals。