遮蔽之后,还能追踪那起事件吗
一句话总结
从日志和导出文件中清除个人信息,不是“让它看不见”,而是保留调查所需的关联性,同时让人认不出来。划定这条边界的,是掩码规则和确定性的假名值,而这一切是否做对了,要用机器重新测量来证明。
为什么需要它
调查故障时,经常需要整份拿到应用程序日志。可是转账 API 的日志里混有自由文本,这些文本里原样写着账号、银行卡号、电话号码和电子邮件,这种情况很常见。并不是开发者怀着恶意写下的,而是为了尽快找到故障,把“请求报文整个”打印出来的某一行,已经留了好几年。
要把这份日志交给分析负责人或供应商,就必须把个人信息清除掉。个人信息保护法 要求对已经达成目的的个人信息及时销毁(第 21 条),而调查所需要的不是人的身份,而是“同一个账户失败了几次”这样的关联性。但如果把账号全部用星号盖住,这种关联性也就消失了。无法区分到底是同一个人失败了三次,还是三个人各失败了一次。必须遮住,却又要能串起来看。同时满足这两个要求,就是这个主题的全部。
工作原理
清洗(scrubbing)分为三个阶段。找出,区分,替换。
找出靠的是正则表达式。用 re 模块 扫描账号的形态、16 位数字、手机号的形态和电子邮件的形态。问题在于,正则表达式只看“形态”。银行日志里,除了银行卡号,16 位数字比比皆是。结算序列号、报文追踪号、批处理作业标识符,全都是 16 位。如果连这些也用星号盖住,调查所需的标识符就消失了。
区分就是在这里登场的。银行卡号带有校验位。从右数第二位开始,每隔一位乘以二,如果乘出的结果大于等于 10,就把各位数字相加(或者减去 9)。把全部加起来,如果是 10 的倍数,就算通过。通常称为 Luhn 算法,作为银行卡号体系标准的 ISO/IEC 7812-1 规定了这种校验位方式(标准原文是收费的,所以本文不提供链接)。随机的 16 位数字碰巧通过这项检查的概率是十分之一,所以加上校验位,误报会减少到十分之一。同时这也意味着,它不会完全消失。
替换同时做两件事。一件是只保留后四位的掩码,让人可以用眼睛确认,另一件是让机器能串起来看的假名令牌。令牌用 hmac 模块 来做。不能直接对值本身做哈希。账号的取值空间很小,单纯的 SHA-256 会被字典攻击还原。必须是使用保密密钥的 HMAC,不知道密钥的一方才无法还原。密钥用 secrets 模块 或者 openssl rand 来生成,不要和日志放在同一个地方。
令牌的消息里要一并放入用途。要让 acct|91231457820 和 card|91231457820 成为不同的值。这样一来,即使其中一边的令牌表泄露了,也无法用来比对另一边。
원본 "계좌 912-31-457820 카드 9012345678901234 일련번호 9900112233445566"
│ │ │
│ │ └ 검사숫자 실패 → 카드 아님 → 그대로 둔다
│ └ 검사숫자 통과 → ************1234
└ ***-**-**7820 + tokens.acct = ["acct_1f3c…"] (같은 계좌면 언제나 같은 값)
在现场相遇的样子
第一,是运行两次就会变样的清洗程序。流水线会重试。对已经遮住的行再次清洗时,如果在 ***-**-**7820 上又数出星号,或者重新计算令牌却得到不同的值,对账就会被破坏。要留下已处理的标记,并让规则不会再次作用于它自己的输出。
第二,是把遮住和删除搞混。只保留后四位的掩码虽然无法还原,但对已经拥有账号列表的人来说,它是缩小候选范围的线索。在只处理少数账户的文件里,仅凭后四位,有时就能确定到某一个人。所以,在导出文件里,不要把掩码后的值本身当作调查键,而要使用令牌。
第三,是没有密钥管理的假名化。如果把密钥放在与日志相同的目录里,或者写死在代码里,或者没人知道密钥是什么时候换的,那么假名值的含义每次都会变。如果在报告里写上密钥标识符(密钥哈希的前几位),日后就能回答“这些令牌是用哪把密钥做的”。
实际工作中真正重要的事
- 正则表达式只给候选。要再用校验位或格式规则过滤一遍,才不会把调查所需的标识符杀掉。
- 同一个值得到同一个令牌,不同的用途得到不同的令牌。这是同时获得可调查性和可分离性的唯一方法。
- 幂等性是一项功能。要用测试把“运行两次,字节是否相同”钉死。
- 不要在检查结果里写原文。如果在泄露报告里原样写出泄露的值,这份报告就成了另一份泄露件。
- 把策略放在文件里。什么不导出,保留几位,保存多久,让文件而不是人来记住。
下一项实验要做什么
亲手做出 640 行的合成转账日志,找出候选,用校验位区分银行卡和序列号,然后做出附有掩码和假名令牌的导出件。接着,一次也不使用原始账号,仅凭令牌追踪事故客户的事件,运行两次确认结果相同,并确认残留的泄露为 0,最后以策略和报告收尾。评分器会用你的密钥重新计算令牌,并用它自己做出的号码,亲自运行你的程序。