随机睡眠可以防止定时攻击吗?

时间:2023-01-13 10:31:35

From Wikipedia

In cryptography, a timing attack is a side channel attack in which the attacker attempts to compromise a cryptosystem by analyzing the time taken to execute cryptographic algorithms.

在密码学中,定时攻击是一种侧通道攻击,攻击者通过分析执行加密算法所花费的时间来试图破坏密码系统。

Actually, to prevent timing attacks, I'm using the following function taken from this answer:

实际上,为了防止定时攻击,我使用了以下从这个答案中获取的函数:

function timingSafeCompare($safe, $user) {
    // Prevent issues if string length is 0
    $safe .= chr(0);
    $user .= chr(0);

    $safeLen = strlen($safe);
    $userLen = strlen($user);

    // Set the result to the difference between the lengths
    $result = $safeLen - $userLen;

    // Note that we ALWAYS iterate over the user-supplied length
    // This is to prevent leaking length information
    for ($i = 0; $i < $userLen; $i++) {
        // Using % here is a trick to prevent notices
        // It's safe, since if the lengths are different
        // $result is already non-0
        $result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i]));
    }

    // They are only identical strings if $result is exactly 0...
    return $result === 0;
}

But I was thinking if is possible prevent this kind of attack using a random sleep like

但我在想是否有可能使用随机睡眠来阻止这种攻击

function timingSafeCompare($a,$b) {
    sleep(rand(0,100));
    if ($a === $b) {
        return true;
    } else {
        return false;
    }
}

Or maybe augmenting the randomness of sleep

或者可能增加睡眠的随机性

sleep(rand(1,10)+rand(1,10)+rand(1,10)+rand(1,10));

This kind of approach can totally prevent timing attacks? Or just make the work harder?

这种方法可以完全防止定时攻击吗?或者只是让工作更难?

3 个解决方案

#1


14  

This kind of approach can totally prevent timing attacks? Or just make the work harder?

这种方法可以完全防止定时攻击吗?或者只是让工作更难?

Neither. It doesn't prevent timing attacks, nor does it make them any more difficult at all.

都不是。它不会阻止定时攻击,也不会使它们变得更加困难。

To understand why, look at the docs for sleep. Specifically, the meaning of the first parameter:

要了解原因,请查看睡眠文档。具体来说,第一个参数的含义:

Halt time in seconds.

停止时间以秒为单位。

So your app takes 0.3 seconds to respond without sleep. With sleep it takes either 0.3, 1.3, 2.3, etc...

所以你的应用程序需要0.3秒才能无需睡眠即可响应睡眠时需要0.3,1.3,2.3等......

So really, to get the part we care about (the timing difference), we just need to chop off the integer part:

所以,真的,要获得我们关心的部分(时序差异),我们只需要切掉整数部分:

$real_time = $time - floor($time);

But let's go a step further. Let's say that you randomly sleep using usleep. That's a lot more granular. That's sleeping in microseconds.

但让我们更进一步。假设您使用usleep随机睡觉。这更加精细。那是在几微秒内睡觉。

Well, the measurements are being made in the 15-50 nanosecond scale. So that sleep is still about 100 times less granular than the measurements being made. So we can average off to the single microsecond:

那么,测量是在15-50纳秒的范围内进行的。因此,睡眠的粒度仍然比测量值低100倍。所以我们可以平均到单微秒:

$microseconds = $time * 1000000;
$real_microseconds = $microseconds - floor($microseconds);

And still have meaningful data.

并且仍然有有意义的数据。

You could go further and use time_nanosleep which can sleep to nanosecond scale precision.

您可以进一步使用time_nanosleep,它可以睡眠到纳秒级精度。

Then you could start fuddling with the numbers.

然后你可以开始拥抱这些数字。

But the data is still there. The beauty of randomness is that you can just average it out:

但数据仍然存在。随机性的美妙之处在于你可以将其平均化:

$x = 15 + rand(1, 10000);

Run that enough times and you'll get a nice pretty graph. You'll tell that there are about 10000 different numbers, so you can then average away the randomness and deduce the "private" 15.

运行足够的时间,你会得到一个漂亮的漂亮图表。你会告诉大约有10000个不同的数字,所以你可以平均掉随机性并推断出“私有”15。

Because well-behaved randomness is unbiased, it's pretty easy to detect statistically over a large enough sample.

由于良好的随机性是无偏的,因此在足够大的样本上进行统计检测非常容易。

So the question I would ask is:

所以我要问的问题是:

Why bother with sleep-like hacks when you can fix the problem correctly?

#2


9  

Anthony Ferrara answered this question in his blog post, It's All About Time. I highly recommend this article.

安东尼费拉拉在他的博客文章“这就是时间关系”中回答了这个问题。我强烈推荐这篇文章。

Many people, when they hear about timing attacks, think "Well, I'll just add a random delay! That'll work!". And it doesn't.

许多人在听到定时攻击时会想到“好吧,我只会添加一个随机的延迟!那就行了!”。它没有。

#3


8  

This is fine for a single request if the only side channel observable by the attacker is the response time.

如果攻击者可观察到的唯一侧通道是响应时间,则这对于单个请求是好的。

However, if an attacker makes enough requests this random delay could average out as noted in @Scott's answer citing ircmaxell's blog post:

但是,如果攻击者提出足够的请求,这个随机延迟可以按照@ Scott的回答中所述,并引用ircmaxell的博客文章:

So if we needed to run 49,000 tests to get an accuracy of 15ns [without a random delay], then we would need perhaps 100,000 or 1,000,000 tests for the same accuracy with a random delay. Or perhaps 100,000,000. But the data is still there.

因此,如果我们需要运行49,000次测试以获得15ns的准确度[没有随机延迟],那么我们需要大约100,000或1,000,000次测试,以获得相同的精度和随机延迟。或许100,000,000。但数据仍然存在。

As an example, let's estimate the number of requests a timing attack would need to get a valid 160 bit Session ID like PHP at 6 bits per character which gives a length of 27 characters. Assume, like the linked answer that an attack can only be done on one user at once (as they are storing the user to lookup in the cookie).

作为一个例子,让我们估计定时攻击所需的请求数量,以获得有效的160位会话ID,如PHP,每个字符6位,长度为27个字符。假设,与链接的答案一样,攻击只能同时对一个用户进行(因为他们将用户存储在cookie中查找)。

Taking the very best case from the blog post, 100,000, the number of permutations would be 100,000 * 2^6 * 27.

从博客文章中获得100,000个最佳案例,排列数量将为100,000 * 2 ^ 6 * 27。

On average, the attacker will find the value halfway through the number of permutations.

平均而言,攻击者会在排列数量的中途找到值。

This gives the number of requests needed to discover the Session ID from a timing attack to be 86,400,000. This is compared to 42,336,000 requests without your proposed timing protection (assuming 15ns accuracy like the blog post).

这使得从定时攻击中发现会话ID所需的请求数量为86,400,000。这与没有您提出的时间保护的42,336,000个请求进行比较(假设像博客文章那样准确度为15ns)。

In the blog post, taking the longest length tested, 14, took 0.01171 seconds on average, which means 86,400,000 would take 1,011,744 seconds which equates to 11 days 17 hours 2 minutes 24 seconds.

在博客文章中,测试的最长时间为14,平均花费0.01171秒,这意味着86,400,000将花费1,011,744秒,相当于11天17小时2分24秒。

Could a random sleep prevent timing attacks?

随机睡眠可以防止定时攻击吗?

This depends on the context in which your random sleep is used, and the bit strength of the string that it is protecting. If it is for "keep me logged in" functionality which is the context in the linked question, then it could be worth an attacker spending 11 days to use the timing attack to brute force a value. However, this is assuming perfect conditions (i.e. fairly consistent response times from your application for each string position tested and no resetting or rollover of IDs). Also, these type of activity from an attacker will create a lot of noise and it is likely they will be spotted via IDS and IPS.

这取决于使用随机睡眠的上下文,以及它保护的字符串的位强度。如果它是“保持登录”功能,这是链接问题中的上下文,那么攻击者花11天时间使用定时攻击来暴力破坏值可能是值得的。但是,这是假设完美的条件(即,对于每个测试的字符串位置,您的应用程序的响应时间相当一致,并且不会重置或转换ID)。此外,攻击者的这些类型的活动会产生很多噪音,很可能会通过IDS和IPS发现它们。

It can't entirely prevent them, but it can make them more difficult for an attacker to execute. It would be much easier and better to use something like hash-equals which would prevent timing attacks entirely assuming the string lengths are equal.

它不能完全阻止它们,但它可能使攻击者更难以执行。使用像hash-equals这样的东西会更容易也更好,这样可以完全假设字符串长度相等,从而防止定时攻击。

Your proposed code

function timingSafeCompare($a,$b) {
    sleep(rand(0,100));
    if ($a === $b) {
        return true;
    } else {
        return false;
    }
}

Note that the PHP rand function is not cryptographically secure:

请注意,PHP rand函数不具有加密安全性:

Caution This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using openssl_random_pseudo_bytes() instead.

注意此函数不会生成加密安全值,也不应用于加密目的。如果您需要加密安全值,请考虑使用openssl_random_pseudo_bytes()。

This means that in theory an attacker could predict what rand was going to generate and then use this information to determine whether the response time delay from your application was due to random sleep or not.

这意味着理论上攻击者可以预测兰德将要生成什么,然后使用此信息来确定应用程序的响应时间延迟是否是由于随机睡眠造成的。

The best way to approach security is to assume that the attacker knows your source code - the only things secret from the attacker should be things like keys and passwords - assume that they know the algorithms and function used. If you can still say your system is secure even though an attacker knows exactly how it works, you will be most of the way there. Functions like rand are usually set to seed with the current time of day, so an attacker can just make sure their system clock is set to the same as your server and then make requests to validate that their generator is matching yours.

处理安全性的最佳方法是假设攻击者知道您的源代码 - 攻击者唯一的秘密应该是密钥和密码之类的东西 - 假设他们知道所使用的算法和函数。如果您仍然可以说您的系统是安全的,即使攻击者确切地知道它是如何工作的,那么您将大部分都在那里。像rand这样的函数通常设置为当前时间种子,因此攻击者可以确保将其系统时钟设置为与服务器相同,然后发出请求以验证其生成器是否与您的生成器匹配。

Due to this, it is best to avoid insecure random functions like rand and change your implementation to use openssl_random_pseudo_bytes which will be unpredictable.

因此,最好避免使用rand等不安全的随机函数,并将实现更改为使用openssl_random_pseudo_bytes,这将是不可预测的。

Also, as per ircmaxell's comment, sleep is not granular enough as it only accepts an integer to represent the number of seconds. If you are going to try this approach look into time_nanosleep with a random number of nanoseconds.

另外,根据ircmaxell的评论,睡眠不够精细,因为它只接受一个整数来表示秒数。如果您打算尝试这种方法,请使用随机数纳秒来查看time_nanosleep。

These pointers should help secure your implementation against this type of timing attack.

这些指针应该有助于保护您的实现免受此类时序攻击。

#1


14  

This kind of approach can totally prevent timing attacks? Or just make the work harder?

这种方法可以完全防止定时攻击吗?或者只是让工作更难?

Neither. It doesn't prevent timing attacks, nor does it make them any more difficult at all.

都不是。它不会阻止定时攻击,也不会使它们变得更加困难。

To understand why, look at the docs for sleep. Specifically, the meaning of the first parameter:

要了解原因,请查看睡眠文档。具体来说,第一个参数的含义:

Halt time in seconds.

停止时间以秒为单位。

So your app takes 0.3 seconds to respond without sleep. With sleep it takes either 0.3, 1.3, 2.3, etc...

所以你的应用程序需要0.3秒才能无需睡眠即可响应睡眠时需要0.3,1.3,2.3等......

So really, to get the part we care about (the timing difference), we just need to chop off the integer part:

所以,真的,要获得我们关心的部分(时序差异),我们只需要切掉整数部分:

$real_time = $time - floor($time);

But let's go a step further. Let's say that you randomly sleep using usleep. That's a lot more granular. That's sleeping in microseconds.

但让我们更进一步。假设您使用usleep随机睡觉。这更加精细。那是在几微秒内睡觉。

Well, the measurements are being made in the 15-50 nanosecond scale. So that sleep is still about 100 times less granular than the measurements being made. So we can average off to the single microsecond:

那么,测量是在15-50纳秒的范围内进行的。因此,睡眠的粒度仍然比测量值低100倍。所以我们可以平均到单微秒:

$microseconds = $time * 1000000;
$real_microseconds = $microseconds - floor($microseconds);

And still have meaningful data.

并且仍然有有意义的数据。

You could go further and use time_nanosleep which can sleep to nanosecond scale precision.

您可以进一步使用time_nanosleep,它可以睡眠到纳秒级精度。

Then you could start fuddling with the numbers.

然后你可以开始拥抱这些数字。

But the data is still there. The beauty of randomness is that you can just average it out:

但数据仍然存在。随机性的美妙之处在于你可以将其平均化:

$x = 15 + rand(1, 10000);

Run that enough times and you'll get a nice pretty graph. You'll tell that there are about 10000 different numbers, so you can then average away the randomness and deduce the "private" 15.

运行足够的时间,你会得到一个漂亮的漂亮图表。你会告诉大约有10000个不同的数字,所以你可以平均掉随机性并推断出“私有”15。

Because well-behaved randomness is unbiased, it's pretty easy to detect statistically over a large enough sample.

由于良好的随机性是无偏的,因此在足够大的样本上进行统计检测非常容易。

So the question I would ask is:

所以我要问的问题是:

Why bother with sleep-like hacks when you can fix the problem correctly?

#2


9  

Anthony Ferrara answered this question in his blog post, It's All About Time. I highly recommend this article.

安东尼费拉拉在他的博客文章“这就是时间关系”中回答了这个问题。我强烈推荐这篇文章。

Many people, when they hear about timing attacks, think "Well, I'll just add a random delay! That'll work!". And it doesn't.

许多人在听到定时攻击时会想到“好吧,我只会添加一个随机的延迟!那就行了!”。它没有。

#3


8  

This is fine for a single request if the only side channel observable by the attacker is the response time.

如果攻击者可观察到的唯一侧通道是响应时间,则这对于单个请求是好的。

However, if an attacker makes enough requests this random delay could average out as noted in @Scott's answer citing ircmaxell's blog post:

但是,如果攻击者提出足够的请求,这个随机延迟可以按照@ Scott的回答中所述,并引用ircmaxell的博客文章:

So if we needed to run 49,000 tests to get an accuracy of 15ns [without a random delay], then we would need perhaps 100,000 or 1,000,000 tests for the same accuracy with a random delay. Or perhaps 100,000,000. But the data is still there.

因此,如果我们需要运行49,000次测试以获得15ns的准确度[没有随机延迟],那么我们需要大约100,000或1,000,000次测试,以获得相同的精度和随机延迟。或许100,000,000。但数据仍然存在。

As an example, let's estimate the number of requests a timing attack would need to get a valid 160 bit Session ID like PHP at 6 bits per character which gives a length of 27 characters. Assume, like the linked answer that an attack can only be done on one user at once (as they are storing the user to lookup in the cookie).

作为一个例子,让我们估计定时攻击所需的请求数量,以获得有效的160位会话ID,如PHP,每个字符6位,长度为27个字符。假设,与链接的答案一样,攻击只能同时对一个用户进行(因为他们将用户存储在cookie中查找)。

Taking the very best case from the blog post, 100,000, the number of permutations would be 100,000 * 2^6 * 27.

从博客文章中获得100,000个最佳案例,排列数量将为100,000 * 2 ^ 6 * 27。

On average, the attacker will find the value halfway through the number of permutations.

平均而言,攻击者会在排列数量的中途找到值。

This gives the number of requests needed to discover the Session ID from a timing attack to be 86,400,000. This is compared to 42,336,000 requests without your proposed timing protection (assuming 15ns accuracy like the blog post).

这使得从定时攻击中发现会话ID所需的请求数量为86,400,000。这与没有您提出的时间保护的42,336,000个请求进行比较(假设像博客文章那样准确度为15ns)。

In the blog post, taking the longest length tested, 14, took 0.01171 seconds on average, which means 86,400,000 would take 1,011,744 seconds which equates to 11 days 17 hours 2 minutes 24 seconds.

在博客文章中,测试的最长时间为14,平均花费0.01171秒,这意味着86,400,000将花费1,011,744秒,相当于11天17小时2分24秒。

Could a random sleep prevent timing attacks?

随机睡眠可以防止定时攻击吗?

This depends on the context in which your random sleep is used, and the bit strength of the string that it is protecting. If it is for "keep me logged in" functionality which is the context in the linked question, then it could be worth an attacker spending 11 days to use the timing attack to brute force a value. However, this is assuming perfect conditions (i.e. fairly consistent response times from your application for each string position tested and no resetting or rollover of IDs). Also, these type of activity from an attacker will create a lot of noise and it is likely they will be spotted via IDS and IPS.

这取决于使用随机睡眠的上下文,以及它保护的字符串的位强度。如果它是“保持登录”功能,这是链接问题中的上下文,那么攻击者花11天时间使用定时攻击来暴力破坏值可能是值得的。但是,这是假设完美的条件(即,对于每个测试的字符串位置,您的应用程序的响应时间相当一致,并且不会重置或转换ID)。此外,攻击者的这些类型的活动会产生很多噪音,很可能会通过IDS和IPS发现它们。

It can't entirely prevent them, but it can make them more difficult for an attacker to execute. It would be much easier and better to use something like hash-equals which would prevent timing attacks entirely assuming the string lengths are equal.

它不能完全阻止它们,但它可能使攻击者更难以执行。使用像hash-equals这样的东西会更容易也更好,这样可以完全假设字符串长度相等,从而防止定时攻击。

Your proposed code

function timingSafeCompare($a,$b) {
    sleep(rand(0,100));
    if ($a === $b) {
        return true;
    } else {
        return false;
    }
}

Note that the PHP rand function is not cryptographically secure:

请注意,PHP rand函数不具有加密安全性:

Caution This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using openssl_random_pseudo_bytes() instead.

注意此函数不会生成加密安全值,也不应用于加密目的。如果您需要加密安全值,请考虑使用openssl_random_pseudo_bytes()。

This means that in theory an attacker could predict what rand was going to generate and then use this information to determine whether the response time delay from your application was due to random sleep or not.

这意味着理论上攻击者可以预测兰德将要生成什么,然后使用此信息来确定应用程序的响应时间延迟是否是由于随机睡眠造成的。

The best way to approach security is to assume that the attacker knows your source code - the only things secret from the attacker should be things like keys and passwords - assume that they know the algorithms and function used. If you can still say your system is secure even though an attacker knows exactly how it works, you will be most of the way there. Functions like rand are usually set to seed with the current time of day, so an attacker can just make sure their system clock is set to the same as your server and then make requests to validate that their generator is matching yours.

处理安全性的最佳方法是假设攻击者知道您的源代码 - 攻击者唯一的秘密应该是密钥和密码之类的东西 - 假设他们知道所使用的算法和函数。如果您仍然可以说您的系统是安全的,即使攻击者确切地知道它是如何工作的,那么您将大部分都在那里。像rand这样的函数通常设置为当前时间种子,因此攻击者可以确保将其系统时钟设置为与服务器相同,然后发出请求以验证其生成器是否与您的生成器匹配。

Due to this, it is best to avoid insecure random functions like rand and change your implementation to use openssl_random_pseudo_bytes which will be unpredictable.

因此,最好避免使用rand等不安全的随机函数,并将实现更改为使用openssl_random_pseudo_bytes,这将是不可预测的。

Also, as per ircmaxell's comment, sleep is not granular enough as it only accepts an integer to represent the number of seconds. If you are going to try this approach look into time_nanosleep with a random number of nanoseconds.

另外,根据ircmaxell的评论,睡眠不够精细,因为它只接受一个整数来表示秒数。如果您打算尝试这种方法,请使用随机数纳秒来查看time_nanosleep。

These pointers should help secure your implementation against this type of timing attack.

这些指针应该有助于保护您的实现免受此类时序攻击。