24

I just came across this code in the HTTP Auth library of the Zend Framework. It seems to be using a special string compare function to make it more secure. However, I don't quite understand the comments. Could anybody explain why this function is more secure than doing $a == $b?

/**
 * Securely compare two strings for equality while avoided C level memcmp()
 * optimisations capable of leaking timing information useful to an attacker
 * attempting to iteratively guess the unknown string (e.g. password) being
 * compared against.
 *
 * @param string $a
 * @param string $b
 * @return bool
 */
protected function _secureStringCompare($a, $b)
{
    if (strlen($a) !== strlen($b)) {
        return false;
    }
    $result = 0;
    for ($i = 0; $i < strlen($a); $i++) {
        $result |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $result == 0;
}
laurent
  • 88,262
  • 77
  • 290
  • 428

1 Answers1

37

It looks like they're trying to prevent timing attacks.

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. 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.

Basically, if it takes a different amount of time to compare a correct password and an incorrect password, then you can use the timing to figure out how many characters of the password you've guessed correctly.

Consider an extremely flawed string comparison (this is basically the normal string equality function, with an obvious wait added):

function compare(a, b) {
    if(len(a) !== len(b)) { 
        return false;
    }
    for(i = 0; i < len(a); ++i) {
        if(a[i] !== b[i]) {
            return false;
        }
        wait(10); // wait 10 ms
    }
    return true;
}

Say you give a password and it (consistently) takes some amount of time for one password, and about 10 ms longer for another. What does this tell you? It means the second password has one more character correct than the first one.

This lets you do movie hacking -- where you guess a password one character at a time (which is much easier than guessing every single possible password).

In the real world, there's other factors involved, so you have to try a password many, many times to handle the randomness of the real world, but you can still try every one character password until one is obviously taking longer, then start on two character password, and so on.

This function still has a minor problem here:

if(strlen($a) !== strlen($b)) { 
    return false;
}

It lets you use timing attacks to figure out the correct length of the password, which lets you not bother guessing any shorter or longer passwords. In general, you want to hash your passwords first (which will create equal-length strings), so I'm guessing they didn't consider it to be a problem.

Brendan Long
  • 53,280
  • 21
  • 146
  • 188
  • 1
    indeed. "don't go there because it's dangerous, but go there anyways, because it's the only road" – Marc B May 14 '12 at 02:14
  • @rook - why is it in correct? From the comments in the code, that is what zend is trying to achieve. – iWantSimpleLife May 15 '12 at 01:31
  • Actually the stolen compare at the start does not provide any more info then without it. if a is the password, it will still take a constant amount of time to run depending on the length. – iWantSimpleLife May 15 '12 at 01:33
  • 2
    @iWantSimpleLife - Consider a situation where you're not hashing the password, and the correct password is 5 characters long. If you submit a password that's 1 character long, it will return immediately. At 2 characters long, it will return immediately, and so on up to 5 characters, at which point it will suddenly take slightly longer, and then at 6 it will return immediately again. Even if `strlen` doesn't return in constant time (like if it takes longer for a longer string), then you could still do a regression and figure out at which length the comparison takes longer. – Brendan Long May 15 '12 at 02:23
  • But won't it be the same for hashing. assuming you are using md5. it take a progressively longer time to hash a longer string. so technically, you can still guess at the length from the time it takes to hash. unless the password is stored pre-hashed in the database. in that case timing attacks are basically useless and you can just do a $a == $b compare. – iWantSimpleLife May 15 '12 at 10:55
  • 5
    @iWantSimpleLife: Passwords should **always** be stored pre-hashed in the database. – SLaks May 15 '12 at 13:15
  • 1
    Yup. but we are discussing the implementation of the function here, not security in general. ;) – iWantSimpleLife May 16 '12 at 01:34
  • @iWantSimpleLife - I already mentioned in my answer that I assume they reason they don't consider it to be a problem is because they're always comparing hashed passwords. I figured I'd point out that the timing attack does still exist, since it's interesting, just not dangerous in the specific case that this function was written for. – Brendan Long May 16 '12 at 20:23
  • @iWantSimpleLife - Also, the assumption with any sort of hashed password is that you store the hashed version. If you're storing the plaintext and hashing right before comparing, then you have much more serious problems than timing attacks. If the correct password is pre-hashed (like you do in any sane implementation), then the fact that a longer password takes longer to hash doens't matter, because you only hash the input from the user -- so they most information they could get from a timing attack is their own input, which presumably they know. – Brendan Long May 16 '12 at 20:27
  • I love the double entendre of "pre-hashed" (before the hash function is applied; the plaintext has already been hashed). So yeah SLaks, store the pre-hashed password ;) – Thomas Eding Sep 10 '13 at 05:40
  • @ThomasEding I mean that the password stored in the database is hashed before storing it instead of hashed when retrieved (which is obviously pointless). – Brendan Long Sep 10 '13 at 15:38
  • Guys, it is not about passwords at all. I came here to find php implementation of Ruby method Rack::Utils.secure_compare for securing GitHub's webhooks. This method needed to verify signature. – Maxim Mazurok Aug 22 '15 at 10:17