What Does Timing Attack Actually Mean?
Timing attacks
Timing attacks are a class of malicious attacks against a product where the length of time that your application takes to perform a task leaks some information. Take, for example, an application that takes in an email and password to check. If there is no user with a provided email address, returns an error in 5ms, but when given a valid email for a user with an incorrect password, returns an error in 500ms.
To an attacker, the difference in times between those two requests can make it relatively obvious if there is a valid email or not. If the difference was more subtle, an attacker can make many requests over a long time and average them together to distinguish different cases.
Is it a big deal?
This might not seem like a big deal, but let’s say I’m trying to find someone’s personal email. I only have their name, and I know they have signed up for your site. I can try a bunch of variations of firstname.lastname@gmail.com or lastname{3digitnumber}@gmail.com and so on until I find their actual email address.
How can we fix it?
To fix this issue, we need to make sure that all code paths take the same amount of time. This means that we should avoid returning early in sensitive parts of the codebase. In the case where we are checking users emails and passwords, instead of returning early if the email wasn’t found, we should check the password against a hardcoded value and then return false .
So in the checking emails example, a typical flow would look something like this:
- Does a user exist with this email address? (1ms)
- If yes, what is their password hash? (1ms)
- Does the password hash match the password provided? (400ms)
This flow is fine when a correct email and password are provided, but it becomes vulnerable to a timing attack in the following scenario:
- Does a user exist with this email address? (1ms)
- If no, return (1ms)
One way to avoid this vulnerability, like I mentioned above, is to make both correct and incorrect flows follow the same procedures to align more closely timing wise:
- Does a user exist with this email address (1ms)
- If no, compare the provided password against a hardcoded password hash (400ms)
- Return false anyways (1ms)
This ensures that the function takes the same amount of time for all inputs, making it harder for an attacker to extract information.
While you should do what you can to protect against timing attacks, you can also add additional protections to be safe. Since subtle timing attacks rely on making a large number of requests, another defense here is rate limiting. By rate limiting the requests, we can make it impractical for an attacker to distinguish between different cases.
When building out authentication flows into applications, it can be easy to overlook these kinds of subtle vulnerabilities like timing attacks in code. Although it might feel strange to intentionally slow down your code, stopping the potential leak of personal information is worth the trade off.