Skip to content

元数据卡

  • 前置知识:第11章(数据治理基础)、Vol 8(安全基础)
  • 预计时间:45 分钟
  • 核心难度:进阶
  • 阅读模式:高度专注
  • 完成标志:能识别数据中的敏感信息、应用基本的安全控制措施,并理解差分隐私的原理

你的进度

数据预言厅的最后一个课题,不是技术问题,而是规则问题。

你收到了来自边境要塞的一封加密信:'最近有情报显示,敌方正在收集我方哨兵的行为轨迹数据。请检查预言厅里存储的所有人员数据——谁有权访问?数据是否被匿名化?保留期限是多久?'

数据隐私和安全不是锦上添花——它是你作为数据管理者的法定义务。

你的任务

你有一个数据集,里面有用户的日志数据。你承诺过这些数据只用做分析,不会外泄。但你无法保证每个拿到数据的人都能遵守同样的标准。这一章解决:如何在分析数据的保护个人隐私,如何在合规框架下做数据科学,以及当数据被泄露时安全控制在哪里失效了。


敏感数据的识别

第一步是识别哪些数据是需要保护的。

python
import pandas as pd
import re

def classify_sensitive_columns(df):
    """对 DataFrame 的列做敏感度分类"""
    classes = {"PII": [], "sensitive": [], "non_sensitive": []}
    
    for col in df.columns:
        col_lower = col.lower()
        
        # 直接个人识别信息(PII)
        if any(kw in col_lower for kw in [
            "name", "email", "phone", "id_card", "passport",
            "ip_address", "device_id", "address"
        ]):
            classes["PII"].append(col)
        
        # 敏感属性
        elif any(kw in col_lower for kw in [
            "salary", "income", "health", "diagnosis",
            "religion", "political", "biometric"
        ]):
            classes["sensitive"].append(col)
        
        else:
            classes["non_sensitive"].append(col)
    
    return classes

df = pd.read_csv("user_data.csv")
sensitivity = classify_sensitive_columns(df)
print("PII 字段:", sensitivity["PII"])
print("敏感字段:", sensitivity["sensitive"])

列名是初步过滤。更准确的方法是用正则匹配实际值——比如邮箱格式、手机号格式、身份证格式。

python
def detect_pii_in_values(series):
    """扫描列中的值是否可能包含 PII"""
    sample = series.dropna().astype(str).head(100)
    
    # 邮箱
    email_count = sample.str.contains(
        r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    ).sum()
    
    # 手机号
    phone_count = sample.str.contains(
        r'^1[3-9]\d{9}$'
    ).sum()
    
    return {
        "email_ratio": email_count / len(sample),
        "phone_ratio": phone_count / len(sample),
    }

数据脱敏

识别出敏感数据后,你需要应用脱敏措施。脱敏不是"删了事"——你需要在保护隐私的保留数据的可用性。

python
import hashlib

def anonymize_data(df):
    df = df.copy()
    
    # 1. 遮挡(Masking)—— 只显示部分信息
    df["email_masked"] = df["email"].apply(
        lambda x: x[:3] + "***" + x[x.index("@"):] if "@" in str(x) else x
    )
    
    # 2. 哈希(Tokenization)—— 不可逆但有唯一性
    df["user_id_token"] = df["user_id"].apply(
        lambda x: hashlib.sha256(str(x).encode()).hexdigest()[:16]
    )
    
    # 3. 泛化(Generalization)—— 缩小精度
    df["age_group"] = pd.cut(df["age"], 
        bins=[0, 18, 30, 45, 60, 120],
        labels=["0-18", "19-30", "31-45", "46-60", "60+"])
    
    # 4. 删除原始 PII 列
    pii_cols = ["email", "user_id", "phone", "address"]
    df.drop(columns=[c for c in pii_cols if c in df.columns], inplace=True)
    
    return df

差分隐私

传统脱敏有一个问题:攻击者可以通过多次查询推断出个人数据。差分隐私保证:无论你的数据集里有没有某个人,查询结果分布几乎一样。

python
import numpy as np

def laplace_mechanism(true_value, epsilon, sensitivity=1):
    """对查询结果添加拉普拉斯噪声以实现差分隐私
    epsilon: 隐私预算(越小,越隐私,越不准确)
    sensitivity: 查询的全局敏感度
    """
    scale = sensitivity / epsilon
    noise = np.random.laplace(0, scale)
    return true_value + noise

# 示例:对一个计数查询加噪
true_count = 1000
epsilon = 0.5  # 中等隐私保护
noisy_count = laplace_mechanism(true_count, epsilon)
print(f"真实值: {true_count}, 加噪结果: {noisy_count:.0f}")

# epsilon 越大,噪声越小
for eps in [0.1, 0.5, 1.0, 5.0]:
    noisy = laplace_mechanism(true_count, eps)
    print(f"  epsilon={eps:.1f}: {noisy:.0f}")

差分隐私的关键设计参数是 epsilon:

  • epsilon < 0.1:强隐私保护,噪声很大
  • epsilon ≈ 1:中等保护
  • epsilon > 10:弱保护,接近原始答案

你可以通过组合多个查询来降低噪声——每次查询消耗一部分 epsilon 预算。用完预算后就不能再查了。

访问控制

谁可以访问什么数据,用访问控制列表(ACL)管理。

数据分级:
  Public     — 可对外开放(聚合报告、统计摘要)
  Internal   — 公司内部可访问(按需授权)
  Restricted — 仅限于指定团队成员(原始用户数据)
  Confidential — 仅限于少数人(含完整 PII 的未脱敏数据)

在代码层面,最常用的控制是列级权限:

python
# 伪代码:列级访问控制
ACCESS_CONTROL = {
    "role_analyst": {
        "can_view": ["mission_id", "region", "success_rate", "duration"],
        "cannot_view": ["user_id", "email"],
    },
    "role_data_scientist": {
        "can_view": ["mission_id", "region", "success_rate", "duration",
                     "user_id_token", "age_group"],
        "cannot_view": ["email", "phone", "address"],
    },
    "role_admin": {
        "can_view": "__all__",
    },
}

def filter_columns(df, role):
    """根据角色过滤可见列"""
    acl = ACCESS_CONTROL.get(role, {})
    if acl.get("can_view") == "__all__":
        return df
    allowed = acl.get("can_view", [])
    return df[[c for c in df.columns if c in allowed]]

数据安全基线

最后,记录几条数据安全的最低标准:

  • 传输加密:所有数据在传输过程中必须使用 TLS
  • 存储加密:敏感数据在存储时必须加密(AES-256)
  • 最小化:只收集和保留必要的数据(数据最小化原则)
  • 最小权限:角色能访问的数据范围仅限于完成任务所需
  • 审计日志:谁在什么时间访问了什么数据,有记录可查

常见陷阱

  • 认为"去标识化"就够安全了。结合辅助信息,大多数去标识化的数据可以被重新识别。
  • 脱敏只在导出时做。如果在处理管道中还用原始 PII,泄漏的风险仍然存在。
  • 差分隐私的 epsilon 设太小,查询结果完全不可用。需要测试不同的 epsilon 值找到隐私和准确性的平衡点。
  • 认为合规只是法律团队的事。数据合规的落地需要工程团队参与设计。

通关挑战

  • 热身:扫描一个你常用的数据集,找出可能需要保护的字段。
  • 挑战:实现一个完整的脱敏管道——识别 PII → 应用脱敏策略 → 验证脱敏效果。记录脱敏前和脱敏后数据可用性的差异。
  • 排障:一个经过差分隐私加噪的查询,分析师抱怨"数据无法用"。你怎么判断是 epsilon 太小还是查询本身有问题?

验收标准

  • 能识别数据集中的 PII 和敏感字段
  • 能应用四种脱敏技术之一并解释各自的优缺点
  • 理解差分隐私的基本原理
  • 知道在数据管道中如何实施访问控制

旅人笔记

数据和隐私不是二选一。你可以分析数据、保护隐私、遵守合规——三者并行。但你需要从一开始就设计进去,而不是最后补上。


下一站预告

数据旅程告一段落。下一站,模型工坊(Vol 13),从数据分析到机器学习——让数据自己说话。

Built with VitePress | Software Systems Atlas