做出一份能带出去的表
目标
完成对混有个人信息的客户数据的调查,并制作一页可以对外发送的内容。 将四项原则(最小化收集、假名化、仅导出汇总、保留记录)分别落实到文件中。
环境
在 /root/pii 下工作。环境中已有 python3、openssl、jq。
原始数据由你直接创建——下面是准备工作,不是任务。
mkdir -p /root/pii && cd /root/pii
python3 - <<'PY'
import csv, random
random.seed(7)
names = ['김민준','이서연','박지호','최수아','정하윤','강도윤','조서준','윤지우']
codes = ['TIMEOUT','DECLINED','INVALID_CARD','NETWORK','OK']
rows = []
for i in range(1, 201):
n = random.choice(names)
rows.append({
'order_id': 'ORD-%04d' % i,
'created_at': '2026-09-%02dT%02d:%02d:00Z' % (
random.randint(1, 7), random.randint(0, 23), random.randint(0, 59)),
'name': n,
'email': 'user%03d@example.com' % i,
'phone': '010-%04d-%04d' % (random.randint(1000, 9999), random.randint(1000, 9999)),
'address': '서울시 어딘가 %d길 %d' % (random.randint(1, 90), random.randint(1, 300)),
'card_last4': '%04d' % random.randint(1000, 9999),
'amount': random.randint(1000, 90000),
'status': random.choice(['FAILED', 'FAILED', 'PAID']),
'error_code': random.choice(codes),
})
with open('orders.csv', 'w', newline='', encoding='utf-8') as f:
w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
w.writeheader(); w.writerows(rows)
print(len(rows), '행')
PY
调查目的只有一个——了解支付失败由哪些原因造成。 姓名和地址并非该目的所需信息。
要创建的文件
slim.csv 조사에 필요한 열만 남긴 사본
pseudo.sh 같은 사람을 묶어야 할 때 쓰는 가명값 생성기
masked.csv 육안 확인용 부분 마스킹본
report.csv 밖으로 나가는 유일한 파일 — 사유별 집계
scan.sh 반출 전 검사기
access.log 무엇을 왜 봤는지
handoff.md 무엇을 내보내고 무엇을 안 내보내는지, 그 근거
评分方式
第 3 步和第 6 步中,评分程序会直接运行你的脚本。
pseudo.sh 같은 값(대소문자·공백만 다름) → 같은 결과여야 한다
키가 다르면 → 다른 결과여야 한다
출력 길이 → 32자 이상이어야 한다
scan.sh 이메일이 든 파일 → 막아야 한다
전화번호가 든 파일 → 막아야 한다
집계만 든 파일 → 통과시켜야 한다
步骤
- 创建原始数据(使用上面的准备代码块)。
slim.csv——只保留调查所需的列,行数保持不变。pseudo.sh——接收一个值,输出一行假名值。密钥通过PII_KEY环境变量接收。masked.csv——将电话号码遮盖为010-****-1234形式,并遮盖电子邮件。report.csv——按原因统计数量。总和必须等于原始数据行数。scan.sh——接收一个文件;若包含个人信息,以非 0 状态码退出。access.log——每次查询都以一行记录时间、对象和目的。handoff.md——写明哪些内容导出、哪些不导出,以及为什么 选择这种遮盖方式。
参考
第 3 步中常见的陷阱有三处。若不进行规范化就哈希,仅大小写不同的 同一个人也无法归为同一对象。若不使用密钥就哈希,可以生成电话号码或 电子邮件候选值并穷举比对。而且如果截断为 8 个字符,会产生碰撞。 评分程序会分别检查三项,因此可以立即知道是哪项失败。
第 7 步的 access.log 中也不要写入个人信息。审计日志同样可能成为泄露途径。
包含个人信息的原始数据
创建原始数据(使用上面的准备代码块)。
原样运行说明中的准备代码块。调查所需列和个人信息列必须同时存在,才能练习筛选。
只取所需的列
slim.csv——只保留调查所需的列,行数保持不变。
不要使用 SELECT *,只选择调查所需的列。分析支付失败原因不需要姓名和地址。不要减少行数,只减少列——不能因为过度缩减而使数据无法使用。
需要将同一个人归为一组时
pseudo.sh——接收一个值,输出一行假名值。密钥通过 PII_KEY
环境变量接收。
使用 openssl dgst -sha256 -hmac "$PII_KEY" -r。必须在哈希之前转为小写并删除空格(规范化),同一个人才会映射为相同值。不要截断输出。
用于人工核对的部分遮盖
masked.csv——将电话号码遮盖为 010-****-1234 形式,并遮盖电子邮件。
电话号码只保留后四位(010-****-1234)。虽然无法逆向恢复,但仍可推测,因此只在需要人工目视核对时使用。
对外发送的只有汇总
report.csv——按原因统计数量。总和必须等于原始数据行数。
用一张“按原因统计数量”的表代替原始 200 行。只有总和等于原始行数,才能说明没有遗漏。
导出前检查器
scan.sh——接收一个文件;若包含个人信息,以非 0 状态码退出。
接收一个文件,若包含个人信息则以非 0 状态码退出。全部阻止同样算失败——只含汇总的文件必须通过。若误报过多,就不会有人使用。
记录查看了什么以及原因
access.log——每次查询都以一行记录时间、对象和目的。
日后被问到“谁查看了这些数据”时,这是保护自己的唯一手段。缺少目的就无法自证。该文件中也不要写入个人信息。
决定哪些内容导出、哪些不导出
handoff.md——写明哪些内容导出、哪些不导出,以及为什么
选择这种遮盖方式。
遮盖方式可按是否可逆区分。说明为什么在相应位置使用每种方式。该文档本身也不能包含个人信息。