LabHub
学习 学习路径 课程

处理客户数据

做出一份能带出去的表

在 LabHub 中继续学习

目标

完成对混有个人信息的客户数据的调查,并制作一页可以对外发送的内容。 将四项原则(最小化收集、假名化、仅导出汇总、保留记录)分别落实到文件中。

环境

/root/pii 下工作。环境中已有 python3openssljq。 原始数据由你直接创建——下面是准备工作,不是任务。

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    이메일이 든 파일 → 막아야 한다
           전화번호가 든 파일 → 막아야 한다
           집계만 든 파일 → 통과시켜야 한다

步骤

  1. 创建原始数据(使用上面的准备代码块)。
  2. slim.csv——只保留调查所需的列,行数保持不变。
  3. pseudo.sh——接收一个值,输出一行假名值。密钥通过 PII_KEY 环境变量接收。
  4. masked.csv——将电话号码遮盖为 010-****-1234 形式,并遮盖电子邮件。
  5. report.csv——按原因统计数量。总和必须等于原始数据行数。
  6. scan.sh——接收一个文件;若包含个人信息,以非 0 状态码退出。
  7. access.log——每次查询都以一行记录时间、对象和目的
  8. 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——写明哪些内容导出、哪些不导出,以及为什么 选择这种遮盖方式。

遮盖方式可按是否可逆区分。说明为什么在相应位置使用每种方式。该文档本身也不能包含个人信息。