strcmp-什么是"二进制安全字符串比较"?这种比较对于定时攻击是安全的吗?
如果没有,我如何比较两个字符串来防止定时攻击?比较字符串的哈希值就足够了吗?或者我必须使用一些库(或自己的代码)来提供恒定的比较时间?
这里写道,定时攻击可以在网络中使用。但是这种类型的攻击会存在于现实世界中吗?或者这种攻击只能用于小型攻击者(如政府),因此通过网络进行的保护是多余的?
"二进制安全"意味着任何字节都可以安全地与strcmp
进行比较,而不仅仅是某些字符集中的有效字符。快速测试证实strcmp
对定时攻击不安全:
$nchars = 1000;
$s1 = str_repeat('a', $nchars + 1);
$s2 = str_repeat('a', $nchars) . 'b';
$s3 = 'b' . str_repeat('a', $nchars);
$times = 100000;
$start = microtime(true);
for ($i = 0; $i < $times; $i++) {
strcmp($s1, $s2);
}
$timeForSameAtStart = microtime(true) - $start;
$start = microtime(true);
for ($i = 0; $i < $times; $i++) {
strcmp($s1, $s3);
}
$timeForSameAtEnd = microtime(true) - $start;
printf("'b' at the end: %.4fn'b' at the front: %.4fn", $timeForSameAtStart, $timeForSameAtEnd);
对我来说,这打印出类似'b' at the end: 0.0634 'b' at the front: 0.0287
的东西。
PHP中的许多其他基于字符串的函数可能也会遇到类似的问题。解决这个问题很棘手,尤其是在PHP中,你实际上不知道很多函数在物理级别上到底在做什么。
一种可能的策略是在将答案返回给调用者/潜在攻击者之前,在代码中随机等待一段时间。更好的是,测量检查输入数据所需的时间(例如,使用microtime
),然后等待随机时间减去该时间量。这不是100%安全的,但它使攻击系统变得更加困难,因为攻击者至少必须多次尝试每个输入,才能过滤掉随机性。
strcmp的问题在于,它取决于实现。如果它对字符串的每个字节进行二进制比较,直到达到其中一个字符串的差值或末尾,那么它很容易受到定时攻击。
现在哈希怎么样
我发现了这个安全问题,我相信它对你来说是正确的答案:https://security.stackexchange.com/a/46215
定时攻击是个神话。
我解释。
在一个相似的文本与另一个不同的文本之间验证文本所需的时间大约是几分之一秒,比如+/-0.1秒(夸大了!)。
然而,攻击者测量此时间所需的时间是:
网络的延迟+0.1秒+系统的延迟(可能是忙于执行某些其他任务)+其他延迟。
所以不,这是不可能的,即使对于局部系统(滞后零),时间间隔的结果也总是不清楚的。
在测试中,假设一种方法和另一种方法之间的差异是1us。
所以,如果我们测试它,结果相差1us,那么我们可以猜出部分数字。
但是,如果还有另一个因素,例如,网络、当前的cpu使用量、当前的cpu周期等等,该怎么办。
即使我们排除了网络,我们也知道大多数操作系统都是多任务的,所以测试必须在具有单任务操作系统的系统或运行单任务的系统中进行,这不是你在野外看到的。即使是嵌入式系统也会同时运行多个线程。
但假设我们在本地(而不是网络)运行,我们在只运行单个任务的计算机上进行演练。但我们还有一个问题,现代CPU不是以恒定的周期运行的,它们根据使用情况(、温度和其他因素)而变化
因此,只有在以下情况下才有可能:
- 它是在本地执行的,没有其他因素
- 它作为一个单独的任务运行,并且没有其他任务在服务器上运行
- cpu一直在运行
即它是ABSURD。
这就是测试
<?php
$text='123456789012345678901234567890123456789012345678901234567890123456789012345678901234';
$compare1='12345678901234567890123456789012345678901234567890123456789012345678901234567890123x';
$compare2='2222222222222222222222222222222222222222222222222222222222222222222222222222222222222';
$a1=microtime(true);
for($i=0;$i<100000;$i++) {
if($compare1===$text) {
// do something
}
}
$a2=microtime(true);
var_dump($a2-$a1);
$a1=microtime(true);
for($i=0;$i<100000;$i++) {
if($compare2===$text) {
// do something
}
}
$a2=microtime(true);
var_dump($a2-$a1);
我花了5分钟才使这个假设无效。
测试内容:
- 它测试一个512位的文本,并与两个测试进行比较,并比较时间
- 这个测试是为了证明这一假设,因此它迫使出现一种非真实的情况,即被比较的第一个文本几乎与第一个测试相同(不包括最后一个字符)
- 它还排除了延迟和其他操作
- (为什么512位,大多数密码都是用128位和256位加密的,512位是我们可以称之为安全的)
这就是结果。
一轮:
- 0.021588087081909
- 0.021672010421753(长时间)
另一次运行:
- 0.021767854690552
- 0.022729873657227(长时间)
和另一次运行:
- 0.021697998046875(长时间)
- 0.021611213684082
再次
- 0.021565914154053(长时间)
- 0.020948171615601
再次
- 0.021995067596436
- 0.0224769115448(长时间)
因此,即使测试被迫验证该点,它也会失败。
即。当其中一个变量未知时,你找不到趋势,而这个因素会影响整个测试。我可以测试100万次,结果会是一样的。特别是,该测试避免了任何变量,如延迟、其他进程、对数据库的访问等。