概念定义
朴素的相等判断在第一个不同字节处停下,耗时暴露了前多少字节匹配。恒时比较读取两个输入的全部字节、把差异异或进累加器,再报告累加器是否为零——工作量相同、时长相同、没有信号。
工作机制
规范形式是遍历全长计算按位差并 OR 进累加器,最后以无分支方式把累加器转成布尔。库版本还要求输入等长或显式处理长度,因为提前返回泄漏长度本身就是漏洞。
使用场景
任一操作数是机密或由机密派生的一切比较:会话令牌查找结果、HMAC 校验、密码重置令牌、webhook 签名、加密 Cookie 完整性标签。
常见误区
“先哈希再 ==”并不等价——摘要的最终比较仍需恒时,哈希洗不掉泄漏。恒时的成本只是一遍固定长度输入,相对网络延迟可忽略。调用点常把比较包进按结果分叉的日志或异常路径,把信号又引回来——要审计整个函数而不是一行。最敏感的端点应在 CI 里用负载下的时序分布验证。
代码示例
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("", ""));
}