概念定义
计算时间是一条输出信道。只要运行时长依赖机密字节——比较提前退出、以机密为索引的表查找——攻击者就能通过测量与建模在毫无密码学突破的情况下恢复这些字节。
工作机制
攻击者提交大量构造输入,用统计把响应时长与对机密的假设关联起来。逐字节早退的比较在“猜测分歧早”时最快,因此每个正确的前缀字节都可通过时长区分;网络抖动靠重复与中位数统计击穿。
使用场景
令牌与 MAC 校验端点、比较哈希的登录流程、密码学实现、以及耗时依赖机密键访问模式的缓存或数据库查找。一切“对机密做相等判断”的代码都值得怀疑。
常见误区
“服务器快就免疫”不成立——攻击者用海量噪声样本平均出单比特信号。对哈希再比较也救不了:摘要的比较本身仍需恒时。加随机延迟只是增加方差,恒时实现才是消除信号。Node 的 crypto.subtle.timingSafeEqual、Python 的 constant_time_eq 就是为此而生;对比无效与有效令牌分支的 p99,若有可测差异就是待消除的信号。
代码示例
package main
import (
"crypto/sha256"
"crypto/subtle"
"fmt"
)
func ConstantTimeEqual(a, b string) bool {
left := sha256.Sum256([]byte(a))
right := sha256.Sum256([]byte(b))
return subtle.ConstantTimeCompare(left[:], right[:]) == 1
}
func main() {
secret := "s3cr3t-token"
fmt.Println(ConstantTimeEqual(secret, "s3cr3t-token"))
fmt.Println(ConstantTimeEqual(secret, "s3cr3t-tokeX"))
fmt.Println(ConstantTimeEqual(secret, "short"))
}
use std::hint::black_box;
fn constant_time_eq(left: &[u8], right: &[u8]) -> bool {
if left.len() != right.len() {
return false;
}
let mut difference = 0u8;
for (a, b) in left.iter().zip(right.iter()) {
difference |= a ^ b;
}
black_box(difference) == 0
}
fn compare_tokens(expected: &str, provided: &str) -> bool {
constant_time_eq(expected.as_bytes(), provided.as_bytes())
}
fn main() {
println!("equal: {}", compare_tokens("token-abc", "token-abc"));
println!("different: {}", compare_tokens("token-abc", "token-abd"));
println!("length mismatch: {}", compare_tokens("token-abc", "token"));
println!("empty: {}", compare_tokens("", ""));
}