这类问题我又碰上了一次。日志脱敏脚本把手机号做 md5 后写入分析库,本来想着不可逆就没问题,安全评审时同事用一份常见号段字典十分钟还原了八成,因为号段固定,候选空间只有十亿量级。

第一反应是「同一号码在各表哈希值相同,可以跨表关联」,验证下来不成立。

原因不止一个,得分开看。手机号空间小,无盐哈希可以直接穷举;号段和归属地信息还能进一步缩小候选范围;脱敏只做了哈希,没有加盐也没有密钥。

动手时先确认哪些字段是直接哈希的

python -c "import hashlib;print(hashlib.md5('13800001111'.encode()).hexdigest())"

随后改成带密钥的 HMAC 并集中管理密钥

python -c "import hmac,hashlib;print(hmac.new(b'k-from-kms','13800001111'.encode(),hashlib.sha256).hexdigest())"

然后按租户派生不同密钥避免跨表关联

python -c "import hmac,hashlib;print(hmac.new(b'k-tenant-a','13800001111'.encode(),hashlib.sha256).hexdigest())"

等前面都确认过,再抽样验证还原不可行并回归脱敏脚本

python test_desensitize.py --field phone --check brute-force

恢复的判定其实很直接。同一号码在不同租户下哈希值不同,用常见号段字典跑一小时无法命中,脱敏后字段仍可用于分组统计。

补一句。小空间字段的无盐哈希等于编码,脱敏要么加密钥做 HMAC 要么直接掩码,别把 md5 当成不可逆。这类问题最好一次改到位,反复小改反而容易把原因搅混。

作者 9823189