时序攻击:为什么不能用 == 比较密码

今天学习 FastAPI 时,细致了解了一下时序攻击(Timing Attack)。核心问题很简单:用 == 比较字符串时,Python 会在第一个不匹配字符处提前返回,攻击者可以通过响应时间的微小差异逐字猜出用户名和密码。

时序攻击是怎么发生的

假设攻击者试图猜出用户名与密码,发送 johndoe / love123

1
2
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...

Python 比较到第一个字母 js 不同时就停止,应用立刻返回「错误的用户或密码」。

接下来攻击者尝试 stanleyjobsox

1
2
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...

这次需要对比到 stanleyjobso 才能判定不匹配,多花费几微秒才返回错误响应。

反应时间对攻击者的帮助

服务器多花了几微秒才返回错误,攻击者就知道开头猜对了一部分字符。然后放弃 johndoe,继续用 stanleyjobsox 这类前缀去尝试。

专业攻击

攻击者不需要手动操作,可以编写每秒执行成千上万次测试的程序,每次找到更多正确字符。利用时间差,几分钟到几小时内就可能猜出正确的用户名和密码。

使用 secrets.compare_digest() 修补

secrets.compare_digest() 会用恒定的时间比较字符串,无论差异出现在哪个位置:

1
2
3
4
5
6
7
8
9
10
11
12
import secrets

current_username_bytes = credentials.username.encode("utf8")
correct_username_bytes = b"stanleyjobson"
is_correct_username = secrets.compare_digest(
current_username_bytes, correct_username_bytes
)
current_password_bytes = credentials.password.encode("utf8")
correct_password_bytes = b"swordfish"
is_correct_password = secrets.compare_digest(
current_password_bytes, correct_password_bytes
)

在代码中使用 secrets.compare_digest(),就可以有效防御这类时序攻击。

Notice: 正常情况下,这里会有一个基于utteranc.es的留言系统,如果看不到,可能要想想办法才能看到。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2012 - 2026 tiaobug.com All Rights Reserved.

鲁ICP备2024124237号-1