TT Lab
开始
学习 学习路径 课程

银行现场的语言

抹去日志中的个人信息,仍能追查事件

在 TT Lab 中继续学习

目标

从转账 API 日志中找出个人信息候选,用校验位过滤掉误报,并通过掩码和确定性假名令牌做出导出件。即便如此,仍要仅凭令牌追踪事故客户的事件,并证明运行两次得到相同的结果,而且没有残留的泄露。

为什么重要

用于故障调查的日志里混有自由文本,其中原样写着账号和银行卡号。不能原样交出去,但如果全部用星号盖住,连“同一个账户失败了三次”这个事实也会消失。 正则表达式只看形态,所以会把 16 位的结算序列号也误认为银行卡。再加上一道校验位,误报就会减少到十分之一,也不会杀掉调查所需的标识符。 给遮住的值配上 HMAC 确定性令牌,同一个账户就总是得到同一个令牌,可以串起来看,而没有密钥就无法还原。清洗流水线会被重试,所以运行两次的结果必须相同。

步骤

  1. 创建并运行 /root/mask/gen_logs.py,生成 /root/mask/app.log(JSON Lines,500 行以上)和 /root/mask/customers.csv(30 名以上客户)。混入 30 个以上账户、40 个以上通过校验位的银行卡、20 个以上通不过校验位的 16 位数字,并让出现最多的一个账户比第二名多出 3 行以上。
  2. 用 /root/mask/find_candidates.py 找出四类候选,全部写入 /root/mask/candidates.json(total、by_kind、items)。
  3. 创建 /root/mask/luhn.py(有效则退出码为 0),把银行卡候选按 card_verified 和 not_card 区分,写入 /root/mask/candidates_luhn.json。
  4. 用 /root/mask/scrub.py 生成 /root/mask/app.scrubbed.log。按下面参考中的规则,遮住账号、银行卡、电话和电子邮件,不是银行卡的 16 位数字则原样保留。
  5. 创建 /root/mask/keys/scrub.key,让 scrub.py 给每一行附上 tokens(acct、card),然后重新生成 /root/mask/app.scrubbed.log。
  6. 求出 app.log 中出现最多的账户的令牌,只看导出件,把 top_account_token、event_count、req_ids、first_ts 和 last_ts 写入 /root/mask/trace.json。
  7. 创建 /root/mask/leakscan.py,确认对导出件再运行 scrub.py 结果相同之后,把检查结果留在 /root/mask/leak_report.json 中。
  8. 创建 /root/mask/policy.json 和 /root/mask/scrub_report.json。报告里的数字要从实际文件里数出来,policy_sha256 是用 sha256 量出的策略文件的值。

参考

生成合成转账日志快照

创建并运行 /root/mask/gen_logs.py,生成 /root/mask/app.log 和 /root/mask/customers.csv。

在现场,数据是先有的,但在这里要由我们自己来做。账户、银行卡和联系方式必须是编造的值,银行卡号的校验位必须正确。也要混入虽是 16 位但校验位不对的结算序列号——下一步的误报指的就是它们。

用正则表达式不漏地找出全部候选

用 /root/mask/find_candidates.py 找出账号、银行卡、电话和电子邮件的候选,写入 /root/mask/candidates.json。

要读取的文件是第 1 步生成的 /root/mask/app.log。这一步的全部就是召回率。误报会在下一步清除,所以 16 位数字先全部放进银行卡候选。line 从 1 开始数,value 必须是那一行里实际存在的字符串。

用校验位区分银行卡和序列号

创建 /root/mask/luhn.py,把银行卡候选按 card_verified 和 not_card 区分,写入 /root/mask/candidates_luhn.json。

要区分的对象,是第 2 步生成的 /root/mask/candidates.json 中的银行卡候选。从右数第二位开始,每隔一位乘以二,结果大于等于 10 就减去 9。全部加起来,能被 10 整除就算通过。评分器会亲自用它自己做出的号码运行 luhn.py,所以必须遵守退出码约定。

按规则遮盖,并保留序列号

创建 /root/mask/scrub.py,生成 /root/mask/app.scrubbed.log。不是银行卡的 16 位数字原样保留。

要读取的文件是 /root/mask/app.log,结果是 /root/mask/app.scrubbed.log。请严格遵守说明参考中的四种替换形态。行数必须与原件相同,amount 这样的数字字段不要动。评分器会从原件重新计算期望值,并对照到每一个字符。

给同一个账户配上同一个假名令牌

创建 /root/mask/keys/scrub.key,让 scrub.py 给每一行附上 tokens,然后重新生成 /root/mask/app.scrubbed.log。

修改并使用第 4 步做出的 /root/mask/scrub.py。如果只对值做哈希,取值空间很小,会被字典攻击还原。必须是使用保密密钥的 HMAC。如果不把用途放进消息里,账号和银行卡数字相同时就会得到同一个令牌。

仅凭令牌追踪事故客户的事件

用出现最多的账户的令牌来调查导出件,生成 /root/mask/trace.json。

只有选账户这件事要看原件。从那以后,只看 app.scrubbed.log 的 tokens.acct 来统计。如果在结果文件里再次写入账号,到目前为止做的事就都没有意义了。

运行两次是否相同,是否没有残留的泄露

创建 /root/mask/leakscan.py,确认对导出件再运行 scrub.py 结果相同之后,留下 /root/mask/leak_report.json。

请看已经遮住的值会不会再次被规则作用,令牌已经存在时会不会重新计算。检查器不能把所有 16 位数字都抓住——如果连序列号也算作泄露,就没有人会使用它了。

留下导出策略和清洗报告

创建 /root/mask/policy.json 和 /root/mask/scrub_report.json。报告里的数字要从实际文件里数出来。

策略的 mask 值是保留的字符数,key_id 是 /root/mask/keys/scrub.key 的 sha256 前 12 位。报告的 masked 是包含重复的笔数,tokens 是互不相同的令牌的个数,policy_sha256 是对整个策略文件量出的值。