这类问题我又碰上了一次。日志脱敏脚本把手机号做 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 当成不可逆。这类问题最好一次改到位,反复小改反而容易把原因搅混。
