4

I am building a web app that allows login with an email and password. I understand that I need to use a timing safe function to compare the passwords to prevent timing attacks. However I realized that the emails might be vulnerable to the same attack. I am unsure how to prevent timing attacks on the emails.

The emails are stored in a database and the database handles finding the user with the matching email:

 SELECT * FROM users WHERE email = $1

This means that the database handles searching through all the users' emails and looking for a match. The internal database function (in this case the db happened to be PostgresSQL) used to compare the emails provided in the WHERE clause with the ones in the database is not timing safe (I assume because that would be really slow and inefficient). This is an issue at login where inputted emails must be compared against the entire (or much of) the database.

How can I prevent timing attacks on email?

JBis
  • 640
  • 5
  • 17
  • 1
    Assuming you have an index on the login id column (... you should...), retrieval should be a constant-time operation; profile, but there's likely no significant difference between there/not there. Note that the bulk of the time for user retrieval is going to be shipping the data to/from the database server itself. – Clockwork-Muse Jan 29 '20 at 00:58
  • @Clockwork-Muse I think the id is irrelevant here. The email is being compared not the id. – JBis Jan 29 '20 at 00:59
  • "login id" is being used as a standin for the email. You're trying to prevent user enumeration. – Clockwork-Muse Jan 29 '20 at 01:01
  • @Clockwork-Muse I'm confused what you mean. – JBis Jan 29 '20 at 01:02
  • @Clockwork-Muse Are you thinking of "constant time" as in O(1) time? If so, then know that the same term is applied to a different concept in some contexts. It means that execution time is independent of the specific value of any (potentially) sensitive input variables. It's an unfortunate name because constant seems to imply that the run time is always the same (or bounded below a constant) but actually only requires that measuring execution time doesn't leak information. – Future Security Jan 29 '20 at 01:33
  • @Clockwork-Muse Sorry if that wasn't the case. I can think of a few reasons why an indexed database wouldn't have constant time access. (Though I'm speculating, since I don't focus on DBs.) Bloom filters, caches, and disk seek time might be an issue, for example. ... Though it might still be hard to exploit a timing attack for certain database formats if the variability is small and the "noise" in the execution time is large. – Future Security Jan 29 '20 at 01:42
  • @FutureSecurity - Yes, there are many things that might (nondeterministcaly) adjust the database timing. The essential problem, however, is that the majority of the retrieval time is going to be spent navigating the set of **all** entries, not the final check whether a particular entry matches, so leaking from database timing is not going to be particularly helpful. – Clockwork-Muse Jan 29 '20 at 17:10
  • Can't you just pause randomly for 1-10ms? we're talking network-level observation right? That should suffice to conceal any small timing diffs resulting from compares. – dandavis Jan 29 '20 at 18:40
  • @dandavis Common misconception. No, https://security.stackexchange.com/questions/96489/can-i-prevent-timing-attacks-with-random-delays – JBis Jan 29 '20 at 18:42
  • @JBis: we're talking for an email lookup here, not passwords. Given that smaller carrot, i think increasing the cost of an attack, potentially vastly, is a good trade off between doing nothing (how most admins handle email lookups) and being paranoid. "Prevent" is a loaded term. Safes are sold by how long they take to breach; just because a top-end safe _can_ be breached by experts doesn't mean it doesn't prevent attacks that a consumer fire safe cannot. – dandavis Jan 29 '20 at 18:57

2 Answers2

2

Interesting question.

Definitions

Just to make sure we're on the same page about definitions, I'm taking "timing attack" according to the wikipedia definition:

Every logical operation in a computer takes time to execute, and the time can differ based on the input; with precise measurements of the time for each operation, an attacker can work backwards to the input.

The way I think about this is that a naively-implemented strcmp will walk through the strings and quit as soon as it finds a char that's different. Something like (pseudocode)

bool strcmp(char[] stringA, char[] stringB) {
  for (i=0; i<len(stringA); i++) {
    if (stringA[i] != stringB[i])
      return false;
  }
  return true;
}

That means that strcmp("aaaaaa", "aaaaab") will take longer than strcmp("aaaaaa", "bbbbbb"). Basically the longer it takes, the "closer" you are to the secret string. Using a "timing attack oracle" (a server that knows the secret value and gives you timing info), an attacker can figure out the first letter, then the second letter, then eventually the whole string.


Timing safe functions for passwords

I feel required to ask why you need a timing safe password compare function? You're not storing passwords in plaintext are you?

If you're storing password hashes and doing a strcmp on two hash values, then timing safety is not a thing you need to think about. (Maybe I'm wrong and you can still do effective timing attack on hashes?)


Timing safe functions for usernames / email addresses

Now to your actual question. Let's start with the threat model. I suppose you're worried about user enumeration attacks.

With something like a password or a cryptographic key, there's one valid value and being able to guess it one character at a time, rather than the whole string at a time, really really speeds up the brute-force.

With usernames / email addresses, there are many valid values. I wonder how much a "closeness" meter actually helps? And close to what? In what order is postgres going to iterate through the keys? I honestly don't know, but my intuition is that it probably doesn't make nearly as big a difference as it does for a cryptographic key.

That said, if we're going to worry about timing attacks, I assume you have already mitigated every other aspect of user enumeration attacks?

  • When a user submits a username & password, you give absolutely no indication whether the username or the password was wrong. Rapid7 has a great article on this topic.
  • You use some sort of rate-limiting or IP banning to prevent repeated login attempts.

In fact, if you have an effective rate-limiting mechanism, then an attacker probably can't do the thousands of queries they need in order to do statistical timing analysis.

Mike Ounsworth
  • 57,707
  • 21
  • 150
  • 207
  • Firstly, I am hashing password. Hashed passwords are susceptible to the same timing attacks as non hashed passwords. In fact, the node crypto timing safe compare function only works on things of the same length because you are supposed to only do them on hashes (which are the same size). – JBis Jan 29 '20 at 01:47
  • Rate limiting doesn't solve anything. It just, at best slows attackers down, but with multiple IPs the rate limiting is in effective for or difficult to implement. – JBis Jan 29 '20 at 01:47
  • 1
    @JBis Can you give a link for timing attacks on hashed passwords? Sounds like I'm about to learn something. – Mike Ounsworth Jan 29 '20 at 01:48
  • @JBis Isn't the whole point of salted hashes with pbkdf2 or argon2 that even if the attacker gets their hands on the whole db, it's hard to crack the passwords. So using a timing attack to learn the hash, which you then have to crack, doesn't sound remotely efficient to me. – Mike Ounsworth Jan 29 '20 at 02:01
  • 4
    Hashing (on the server, obviously) has been recommended as a standard mechanism to defeat timing attacks, actually. Because the attacker cannot control one byte at a time of the hash digest while keeping all preceding bytes the same (at least, not for the full digest length; this would require a preimage attack on the hash function), and furthermore the attacker doesn't know the per-user salt and therefore cannot compute the digest themselves at all, they cannot launch a linear-time attack on the password even if given perfect timing data. – CBHacking Jan 29 '20 at 02:01
  • @CBHacking Sounds like you could turn that into a much less rambly answer than mine. I'd upvote it! – Mike Ounsworth Jan 29 '20 at 02:03
  • 2
    @MikeOunsworth If the attacker could use a timing attack to extract the password digests, this would be a very serious security issue. Not as serious as getting the unhashed passwords out, but still very bad; most user passwords aren't going to withstand a dictionary attack on the hash no matter how expensive it is (it can't be too expensive for the login server). However, a timing attack assumes the attacker can submit candidate strings for comparison (hashes), so you can edit one byte at a time. If the input is hashed on the server side, changing a single byte will completely change the hash – CBHacking Jan 29 '20 at 02:06
  • @MikeOunsworth Your aren't entirely wrong. CBHacking explains it well. However I'll provide two links: https://security.stackexchange.com/a/9193/163099 https://crypto.stackexchange.com/a/25609 – JBis Jan 29 '20 at 02:15
  • However, emails aren't stored as hashes so they are still susceptible. Maybe storing an email hash is the solution. – JBis Jan 29 '20 at 02:16
  • This explains everything we are discussing very well https://stackoverflow.com/a/27029501/7886229 . We are both right. Hashes are susceptible to timing attacks but it doesn't matter. – JBis Jan 29 '20 at 02:19
  • ...note that most modern password-handling libraries which handle the hashing/validation for you will _already_ do a timing-safe comparison. – Clockwork-Muse Jan 29 '20 at 17:03
  • @Clockwork-Muse Assuming they all hash (which they should) why do they do timing safe comparisons? – JBis Jan 29 '20 at 17:12
  • To help prevent exfiltration of the hash, even if the hash itself isn't as useful. It's about defense in depth, and it's a simple one to add that doesn't require much extra time during validation (_creating_ the hash occupies far more time). – Clockwork-Muse Jan 29 '20 at 18:37
2

From the comments:

Store a second column with the hash of the email. When a user submits their email and password, hash the input email and look it up in the db. This will mitigate timing attacks to the point where it is insignificant.

How hashing mitigates timing attacks is discussed on the below post:

https://stackoverflow.com/a/27029501/7886229

JBis
  • 640
  • 5
  • 17
  • That post discusses how it somewhat mitigates timing attacks, but an email address is not the same as an unguessable password, and they mention you should use a secret salt (the proper name for this is pepper, by the way). None of that is in your answer. – Luc Sep 02 '21 at 23:17