3

Suppose you have the following code in Node:

const { token } = req.body
const hash = crypto.createHmac('sha256', SECRET).update(token).digest('hex')
const user = await User.findById(req.session.userId)

if (hash === user.rememberMeHash) {/*...*/}

The string comparison above is deemed vulnerable to a timing attack because it can leak the character position on a mismatch, so the correct way is

// Hashes are already equal in length because the same hash function was used
if (crypto.timingSafeEqual(new Buffer(hash), new Buffer(user.rememberMeHash))

While true in principle, I can't see how this leak is practically possible. To get reliable time measurements, you'd need to

  • isolate the code snippet to avoid interference from side effects (request handling, Express routing, DB queries);
  • run a large number of empirical tests in a strictly identical environment (same CPU & memory usage, processes, OS);
  • have access to a local server instance that has no traffic or intervention from outside.

None of these are realistic in a distributed system, much less to an attacker with no privileged access and no knowledge of the specific hashing algorithms and secret keys employed.

In practice, you will necessarily get varying and inconsistent results when timing any code, particularly one that is just-in-time compiled like JavaScript. This is well understood in algorithm analysis which doesn't directly measure algorithm runtime because these measurements are acutely sensitive to the underlying hardware, software, compiler, language, etc. In this particular case, compared to a database query or a network call (or even script processing when running node binary on a .js file), string comparison takes a minuscule amount of CPU time to process.

Now, also consider that the above code runs across a cluster of servers behind a load balancer. As such, HTTP response times will vary depending on other incoming and ongoing requests (i.e. website traffic), background processes, hosting provider uptime, network fluctuations (e.g. speed drops), use of Tor or a VPN, and hundreds of other factors.

Considering a real-world web server architecture, how can a mere string comparison ever be exploited in a timing attack?

  • I assume you mean over the internet rather than just a web server, as local networks can have low enough latency that it may be possible, You can average out the time by sending the same request several times. Over the internet there are enough factors to make it difficult to do. Also different deployments affect vulnerabilities, you need to consider contextual risk, so you should only consider the deployment model that applies to you. – wireghoul Dec 15 '19 at 3:15
  • 2
    Does this answer your question? Are string comparson timing attacks practical over public networks – Peteris Dec 15 '19 at 13:55
  • 1
    While string comparison takes a tiny amount of CPU time to process, tiny variations in speed (~100 nanoseconds?) can be measured over a network. A nontrivial number of requests is required, but you don't need to isolate the code snippet nor have an access to a local server nor need it to be completely isolated from other requests. – Peteris Dec 15 '19 at 13:58
  • 2
    @Alex it isn't premature optimization because security != optimization. While it may not be easy to exploit it over the internet, attacks rarely occur as a singularity and it may be exploitable through SSRF or from another compromised server in the DMZ. Or perhaps your website is migrated to a single more powerful server in the future? As I said to understand the risk it poses to your system you would need to consider it in the context of your system. – wireghoul Dec 15 '19 at 15:19
  • 2
    See also Remote Timing Attacks are Practical (PDF). – Sjoerd Dec 16 '19 at 11:29
3

Timing attacks are possible even if response times seem inconsistent, because you can use statistical analysis to filter out a lot of noise in the data. Average values are much more stable than instantaneous values, for example. A fixed delay (like the one introduced by the comparison) will be added to the average, while inconsistent delays (like the ones introduced by the network) will cancel out. Example:

Response times: 17, 20, 1, 15, 62, 35   
Average: 25

Response times (+1 delay): 18, 21, 2, 16, 63, 36
Average: 26

So as you can see the fixed delay is detectable in the average value, and instantaneous spikes don't matter. Finding a stable average value might be difficult though, and it might require a lot of attempts (thousands or even millions of requests). Also the average value might not even be stable enough, and other statistical analysis might be taken into account.

This complexity makes remote timing-attacks impractical in many situations, although research has shown that they are possible within local networks. This means that an attacker might well try to use such timing-attacks once they have broken into your local network, for example. On the other hand, it might be impossible to use the same attack from a remote location, or it might not be worth it at all anyway, depending on several factors like the configuration of the network and your server. A remote attacker will therefore focus on other vulnerabilities to exploit. Also remember that you can avoid this problem by implementing some kind of rate-limiting in your server, banning any potential attacker after too many attempts.

|improve this answer|||||
  • I think this is a good answer. The only critique I'd have is that the scale of the additional time matters. The smaller the difference in time, relevant to noise, the larger the amount of data you'll need to compare. If the time difference in comparison is 1 nanosecond, and your noise is in milliseconds, how much data do you need to find the signal through the noise? – Steve Sether Dec 17 '19 at 17:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.