One (more) word regarding password comparisons on OutSystems

Security is a must from the start of any web-connected application, and hopefully, one will live long enough to not see that much of a bad ending. As security techniques, ciphers, security protocols, and technology move forward, so do the villains. It is up to you whether you want to develop your next big hit App for yesterday, or think ahead of time and convince your clients that yesterday is yet too early. One common pitfall in the coding world is the comparison between two secret strings, namely two passwords: one given by the user being authenticated and the other one, usually hashed, stored in a database.

Something you know; something you own; something you are

The simplest algorithm for a comparison between two passwords might wrongly be assumed of a comparison between two strings. The worst way of doing it would be as described by the following pseudo-code:

function ComparePassword(GuessedPassword, RealPassword)  if length(InPassword) <> length(RealPassword)    return GuessedPassword is Wrong  /*now we know that from this point on, both the GuessedPassword and RealPassword have the same length*/  for ithPosition from 0 to length(GuessedPassword)-1    if GuessedPassword[ithPosition] <> RealPassword[ithPosition]      return GuessedPassword is Wrongreturn GuessedPassword is Correct

This simple and optimized string comparison function might be what we just need. But is highly insecure against a well-known kind of attack: the Timing-attack.

At first, a brute force attack would be unfeasible, for the attacker had to try out every possible combination of characters for each password length. Assuming that a password has only printable Ascii characters, starting from the blank space (decimal 32) to the tilde (decimal 126), each character on our password might be one of 95 possible characters. So, assuming the correct password has exactly 12 characters, it would need at most 95 to the power of 12, an astonishing 540 360 087 662 636 962 890 625 possibilities to be tried out. Therefore, it is an exponential complexity problem to be solved via pure brute force approach; and if we hadn’t narrowed the number of characters on the password things would be even worst. But, the devil is in the details. Can we exploit the time elapsed in ComparePassword?

The use case for such an attack, takes place in an OutSystems environment, attempting to authenticate a user not present on the OutSystems common User Entity, disabling us from using the well-known User_Login server action that has abusive login prevention mechanisms. Imagine that one could potentially measure the time that the ComparePassword function takes to execute. An intruder would try out the following way of guessing the real password:

initialize GuessedPassword = “XXXXXXXXXXXXXXXX”for ithPosition from 0 to length(GuessedPassword)-1  for char in each character on Asci    start timer    GuessedPassword[ithPosition] = char    try out GuessedPassword    end timer

Register time elapsed for that character
if differences on any timer measures GuessedPassword[ithPosition] = char with longest time match else return GuessedPasswordreturn no password found

This attack is based on the fact that, if the ith character is correct, the ComparePassword will take a little more time to execute, for it is trying to check the correctness of the ith+1 character. Ideally, this algorithm execution has linear complexity with N (the length of the true password): O(95*N), meaning O(N). Of course, one might need to measure the duration of the GuessedPassword more than one time, to assure that the little latency resulted is not due to the machine skewness of CPU processing another computation, disk or RAM accesses in between.

Ok…, if there is such time auditing in our machine, directly or indirectly, how do we prevent such attack from taking place?

The answer lies in a string comparison that takes… always the same time, independently of the first one, two, three,… N characters of the guessed password match the correct one or not! One good implementation in OutSystems lies on the CryptoAPI extension. On its C# code, there is a function that takes the same amount of time evaluating the GuessedPassword against the TruePassword.

public static bool equalBytes(byte[] b1, byte[] b2){  int minLen = (b1.Length > b2.Length) ? b2.Length : b1.Length;  bool res = b1.Length == b2.Length;  for (int i = 0; i < minLen; i++)  {    res = res && (b1[i] == b2[i]);  }  return res;}

In the code snippet above, we can see that despite the lengths of both passwords might differ, the function will not stop yet. After the length comparison, all the characters regarding the smallest password will be compared and filtered with a logical AND gate. If any character from the guessed password is different from the true one, res var will hold the FALSE value, but the function will carry on, till the end of the smallest string is reached. It is at the end of all comparisons, and only there, that the function will spit out whether the guessed password was correct or not.

But what if your web application can be easily exploited by trying out passwords from a really extensive list, a list that contains the most commonly used passwords by the users: the Online Dictionary Attack? Check this link, if you think your password might be at risk right now.

To quickly brief you, these dictionaries are of the order of millions of passwords, a much much smaller number from the brute force attack scenario. One good guessed password by the attacker is just what he needs to foul security of most web applications. Don’t use the same password across multiple Apps! Some of the most popular Dictionary Attack utilities like Hashcat or John the Ripper have CPU and memory optimizations in place. The question is: Is there any way to prevent these attacks? We could start by educating the users to not choosing the most common used passwords on the web… but that can be annoying and not practical to the end-user. So, what about slowing down the password comparison say, from 1 millisecond to 1 tenth of a second? Not much of a big deal you might think.

It turns out that we can change the problem of password matching from a simple string comparison to… some really and complex computation, regarding Memory and/or CPU, that needs to be performed to succeed the password match. By crunching the numbers, if we have 100 million passwords on our dictionary and each attempt to fake authenticate to the server takes one-tenth of a second, it would take about 19 years at most to guess the password of the target user going through the dictionary.

On the very same extension CryptoAPI mentioned above, on the comparePassword server action, if the string password is preceded either with $pbkdf2$,$2a$ or $s2$, the respective key derivation function (KDF) will kick in to compare both the provided password and the hash of the true password stored in the database. These KDFs either slow down the password comparison, or use memory-intensive operations like allocating a big table, just intended to make the attacker’s life much difficult, but not enough to make the real user(s) frustrated. Is not like a real user will be failing a password thousands of times per second, will he?

In the end, all the developers have to do for their project not to be a bust is… to start with security in mind! One wrong move right from the beginning may dictate the fate of your App!

--

--