Skip to content

元数据卡

  • 前置知识:第1章(加密与哈希)、第3章(认证与授权)、基础法律概念
  • 预计时间:45 分钟
  • 核心难度:进阶
  • 完成标志:能解释 GDPR 的核心原则和数据处理的法律基础,能区分假名化和匿名化,能设计一个符合隐私保护的数据留存策略

你的进度

你从加密咒语出发,一路走到驿站应用安全、操作法阵隔离和魔力管道监控。你把魔法驿道的每一道防线都立起来了。

但还有一个问题比 SQL 注入更微妙:你存储的那些数据——传送日志中的坐标信息、信使的名字和联系方式、通信记录中的时间戳——这些数据本身就有价值,而且它们和具体的人关联在一起。

这不是咒语漏洞的问题。这是隐私保护的问题。如果驿道被攻破,攻击者拿到的不仅仅是系统控制权——还有所有关联的隐私数据。更糟糕的是,即使驿道没被攻破,只要你在不必要的地方收集了不该收集的数据,你就已经违反了法师议会的隐私法规。 你的任务

理解数据隐私的核心法律框架(GDPR)、技术实现手段(加密存储、假名化、匿名化)、以及数据生命周期管理(收集、使用、留存、删除)。这些不只是"法律团队的事"——工程师的决定直接影响数据保护能力。

本章分层

  • 必读:GDPR 的七项原则、数据权利、假名化 vs 匿名化、数据留存策略
  • 选读:隐私影响评估(DPIA)、数据可携带性(Data Portability)
  • 进阶:差分隐私(Differential Privacy)、同态加密的基本概念

破局 · 溯源

问题:你开发的巡逻报告系统存储了每条巡逻路线的 GPS 坐标、执行哨兵的 badge ID、报告时间、备注内容。你觉得这是"正常的业务需要"——直到有一天,一个哨兵问:"你们为什么存了我的巡逻路线?3 月以前的数据我能删掉吗?"

你不知道答案。你想不通——数据存了好好的,又不占多大空间,为什么要删?

但问题没那么简单:

  • 这些数据和具体的个人关联了(通过 badge ID -> 真实姓名)
  • 你没法回答"为什么存这些数据"
  • 你没法回答"数据要存多久"
  • 你没法回答"用户要求删除数据怎么操作"
  • 如果数据库被泄露,这些数据直接关联到具体的人的日常活动规律

第一块:GDPR 核心原则

GDPR(General Data Protection Regulation)是 2018 年生效的欧盟数据保护法规。它的影响远超出欧盟——只要你的服务面向欧盟居民,或被欧盟居民使用,就需要遵守。

GDPR 的七项核心原则(Art.5):

原则含义工程影响
合法、公平、透明有明确的法律基础收集数据,告诉用户你在收集什么需要收集同意(consent),编写隐私政策
目的限制数据只用于当初告知的目的不要跨用途使用数据
数据最小化只收集与目的相关的数据少存,不是多存
准确性数据应该准确、及时更新提供数据修改功能
存储限制数据只保留必要的时间设计自动数据删除策略
完整性和机密性用适当的安全措施保护数据加密、访问控制、审计日志
问责制能证明你遵守了上述原则记录数据处理活动,做 DPIA

法律基础(Art.6):

在 GDPR 下,你必须有一项合法的法律基础才能处理个人数据。常见的法律基础:

法律基础适用场景示例
同意(Consent)用户主动同意订阅邮件通知
合同履行(Contract)数据处理是合同的一部分分配巡逻任务需要哨兵的岗位 ID
法律义务(Legal Obligation)法律要求保留税务数据保留 7 年
合法利益(Legitimate Interest)双方都合理的利益反欺诈检测(但不能滥用)

大多数时候,对于非必需数据的收集,你需要用户的明确同意。而且同意必须是:

  • 主动的:不能是 pre-checked box
  • 明确的:用户知道他们同意什么
  • 可撤回的:用户随时可以撤回同意
  • 容易撤回的:撤回同意不能比同意更难

第二块:用户的数据权利

GDPR 赋予了用户八项数据权利(相关章节括号内):

1. 知情权(Art.13-14):知道哪些数据被收集了、为什么收集、存多久
2. 访问权(Art.15):我自己的数据,给我一份副本
3. 更正权(Art.16):我的年龄/电话写错了,帮我改
4. 删除权 / "被遗忘权"(Art.17):把我的数据删了
5. 限制处理权(Art.18):先别处理我的数据,等我核实
6. 数据可携带权(Art.20):把我的数据转给另一个服务商(结构化、常用、机器可读格式)
7. 反对权(Art.21):我不想被个性化推荐
8. 自动化决策相关权利(Art.22):不让我被算法自动决定

作为一个工程师,你需要保证系统能实现其中每一个权利。尤其是删除权(Right to Erasure,也被称为"被遗忘权")。

python
# 设计数据删除功能(API 层面)

@app.delete("/api/v1/users/{user_id}/data")
@require_auth
async def delete_user_data(user_id: str, current_user = Depends(get_current_user)):
    """实现 GDPR '被遗忘权'"""
    
    # 确认请求者是用户本人或其授权代理人
    if current_user.id != user_id:
        raise HTTPException(403, "只能删除自己的数据")
    
    # 1. 从生产数据库删除个人数据
    db.execute("DELETE FROM user_profiles WHERE user_id = %s", (user_id,))
    
    # 2. 从备份中标记待删除(备份恢复后自动清理)
    db.execute(
        "INSERT INTO deletion_queue (user_id, scheduled_for) VALUES (%s, NOW())",
        (user_id,)
    )
    
    # 3. 从日志中脱敏(日志可能需要保留,但脱敏掉 PII)
    db.execute(
        "UPDATE audit_logs SET user_id = 'ANONYMIZED', details = 'ANONYMIZED' "
        "WHERE user_id = %s",
        (user_id,)
    )
    
    # 4. 从搜索索引中删除
    # await search_index.delete_document(user_id)
    
    # 5. 从缓存中删除
    # await cache.delete(f"user:{user_id}")
    
    # 6. 记录删除操作(用于合规审计)
    db.execute(
        "INSERT INTO compliance_audit (action, target_user, timestamp) "
        "VALUES ('GDPR_DELETION', %s, NOW())",
        (user_id,)
    )
    
    return {"status": "deleted", "note": "数据将在备份保留期后彻底删除"}

注意这里的难点:备份中的数据。你不会因为一个用户要求删除数据而去重写你的备份磁带。GDPR 并不要求你破坏备份介质——而是要求你确保在恢复备份后,被请求删除的数据不会被重新导入生产环境。通常的做法是维护一份删除名单(deletion manifest),数据恢复后再根据名单清理。

第三块:假名化与匿名化

数据保护的核心矛盾:你想分析数据,但又不能把数据和具体的人关联起来。两种技术路线:

假名化(Pseudonymization):

python
# 假名化:用随机标识替换直接标识符
# 你可以恢复映射关系(如果你有映射表)

import hashlib
import secrets

class Pseudonymizer:
    def __init__(self):
        # 安全的随机盐,每个项目不同
        self.salt = secrets.token_hex(16)
    
    def pseudonymize(self, identifier: str) -> str:
        """把用户标识变成不可逆推的假名"""
        # HMAC 保证相同输入得到相同假名(可关联同一用户的不同记录)
        # 但同时无法从假名反推原始值(即使知道算法,因为 salt 保密)
        return hashlib.sha256(
            (self.salt + identifier).encode()
        ).hexdigest()[:16]

p = Pseudonymizer()

# 原始:badge_10086, badge_10087, badge_10088
# 假名化后:a1b2c3..., d4e5f6..., g7h8i9...

# 优势:你仍然能基于同一用户的记录做分析(同一个假名)
# 劣势:如果盐泄露,可以反推(salt 相当于密钥)
#      如果攻击者有多条记录,可以构建用户画像

匿名化(Anonymization):

匿名化是不可逆的——处理后的数据无法再关联回具体个人。一旦经过真正的匿名化,GDPR 不再适用。

python
# 聚合 + 泛化是目前实际工程中最常用的匿名化方法

def anonymize_location_data(records, min_k=5):
    """
    k-匿名化:确保每个输出组至少有 k 条记录
    
    输入:精确 GPS 坐标 + 时间戳
    输出:栅格区域标识 + 日期(没有精确时间和精确位置)
    """
    def generalize(lat, lng):
        # 把精确 GPS 坐标映射到 1km x 1km 的栅格
        grid_lat = round(lat, 2)  # ≈ 1.1km 精度
        grid_lng = round(lng, 2)
        return f"{grid_lat}_{grid_lng}"
    
    def generalize_time(ts):
        # 只保留日期,丢掉时分秒
        return ts.date().isoformat()
    
    anonymized = []
    for record in records:
        anonymized.append({
            "area": generalize(record["lat"], record["lng"]),
            "date": generalize_time(record["timestamp"]),
            # 丢掉 user_id 和其他身份标识
        })
    
    # 检查 k-匿名化条件
    from collections import Counter
    counts = Counter((r["area"], r["date"]) for r in anonymized)
    for key, count in counts.items():
        if count < min_k:
            print(f"警告:{key} 只有 {count} 条记录,不足以满足 k={min_k} 匿名化")
    
    return anonymized

假名化 vs 匿名化:

特性假名化匿名化
可逆性可恢复(有映射表或 salt)不可逆
GDPR 适用仍适用(仍是个人数据)不适用
分析用途可用于同一用户追踪分析只能做聚合统计
典型技术哈希 + 盐、加密、tokenizationk-匿名化、l-多样性、差分隐私
风险salt 泄露 = 数据还原重识别攻击(多数据源关联)

差分隐私(Differential Privacy):

匿名化的强大版本——在查询结果中加入精心控制的噪声,使得攻击者无法推断某条记录是否存在。

Apple、Google、Apple、和很多调查机构已经使用差分隐私来收集用户统计信息。

python
# 差分隐私:添加拉普拉斯噪声

import numpy as np

def epsilon_dp_query(true_count, epsilon=1.0, sensitivity=1):
    """
    epsilon 差分隐私
    - 更小的 epsilon = 更强的隐私保护(更多噪声)
    - epsilon 0.1-1.0: 强保护
    - epsilon 10+: 弱保护
    """
    # 拉普拉斯噪声
    noise = np.random.laplace(0, sensitivity / epsilon)
    noisy_count = true_count + noise
    return max(0, round(noisy_count))  # 计数不能为负

# 原始数据:有多少哨兵在今天 14:00-15:00 之间经过要塞北门?
true_count = 47  # 精确数字

# 用户查询时,返回含噪声的结果
print(f"实际人数: {true_count}")
print(f"差分隐私结果: {epsilon_dp_query(true_count, epsilon=0.5)}")
print(f"差分隐私结果: {epsilon_dp_query(true_count, epsilon=0.5)}")
# 每次查询返回不同结果(但统计上平均接近真实值)
# 攻击者无法用多次查询确定精确值

差分隐私的代价:精度损失。epsilon 越小,噪声越大,统计结果越不准确。

第四块:数据生命周期管理

数据不是一直保留就有价值的。保留太久反而增加风险。

收集 ─→ 使用 ─→ 存档 ─→ 删除
 │        │       │       │
 │        │       │       └── 合规删除(不可恢复)
 │        │       │
 │        │       └── 归档存储(冷数据,不用于日常操作)
 │        │
 │        └── 活跃使用(正常业务操作)

 └── 数据分类 + 合法同意

数据分类示例(结合 GDPR):

python
# 数据留存策略伪代码
DATA_RETENTION_POLICY = {
    "patrol_reports": {
        "retention_days": 365,  # 最多保留一年
        "anonymize_after_days": 90,  # 90 天后自动匿名化
        "legal_basis": "legitimate_interest",
        "purpose": "巡逻路线分析",
    },
    "audit_logs": {
        "retention_days": 730,  # 安全审计日志保留 2 年
        "anonymize_after_days": None,  # 不匿名化——审计需要完整性
        "legal_basis": "legal_obligation",
        "purpose": "安全审计和合规",
    },
    "user_sessions": {
        "retention_days": 30,  # Session 日志最多一个月
        "anonymize_after_days": 7,
        "legal_basis": "consent",
        "purpose": "用户活动记录",
    },
}

def apply_retention_policy():
    """定时任务:清理过期数据"""
    for table, policy in DATA_RETENTION_POLICY.items():
        # 删除超过保留期的数据
        if policy["retention_days"]:
            db.execute(f"""
                UPDATE {table}
                SET deleted_at = NOW()
                WHERE created_at < NOW() - INTERVAL %s DAY
            """, (policy["retention_days"],))
        
        # 匿名化超过阈值的记录
        if policy.get("anonymize_after_days"):
            db.execute(f"""
                UPDATE {table}
                SET user_id = 'ANONYMIZED',
                    ip_address = NULL,
                    details = REGEXP_REPLACE(
                        details,
                        '([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{{2,}}|'
                        '[0-9]{{1,3}}\\.[0-9]{{1,3}}\\.[0-9]{{1,3}}\\.[0-9]{{1,3}})',
                        '[REDACTED]'
                    )
                WHERE created_at < NOW() - INTERVAL %s DAY
                  AND user_id != 'ANONYMIZED'
            """, (policy["anonymize_after_days"],))

这个定时任务可以跑在 cron 或定期工作流中。重要的是:自动执行,不要依赖人工操作

数据保护影响评估(DPIA):

GDPR 要求,对于可能对个人权利和自由产生高风险的数据处理,必须事先做 DPIA。

DPIA 回答以下问题:

  1. 处理什么数据?处理方式是什么?
  2. 处理的必要性和相称性(为什么要处理这些数据?)
  3. 对个人隐私的风险是什么?
  4. 采取了什么缓解措施?

一些需要进行 DPIA 的场景:

  • 对个人特征进行自动化评估(如信用评分)
  • 对大规模的特殊类别数据(健康、宗教信仰等)的处理
  • 对可公开访问区域的系统性大规模监控

常见陷阱

  • "存储最小化"被忽略。 最常见的违规——收集所有能收集的数据,因为"可能以后用得上"。GDPR 明确要求只收集处理目的所需的数据。
  • 删除时忽略了备份和日志。 用户要求删除数据,你删了生产数据库的记录,但备份磁带和访问日志中仍然保留原始数据。真实的"被遗忘权"实现需要覆盖所有数据副本。
  • 认为匿名化就是去掉名字和邮箱。 只去掉显式标识符(姓名、邮箱)不叫匿名化。结合准标识符(年龄、性别、邮编、职位)可以大概率重识别个体。Netflix 曾在 2006 年发布"匿名化"的评分数据,研究者通过关联 IMDb 数据重识别了用户。
  • 假名化和加密被当作"万无一失"。 假名化数据仍是 GDPR 下的个人数据。如果攻击者通过其他数据源可以关联回个人,假名化失去了意义。
  • 没有数据处理记录。 GDPR 要求组织记录数据处理活动(Art.30 记录义务)。不知道自己的系统里有什么数据、数据流向哪里、谁可以访问——这就是不合规。
  • 隐私政策写成了法律文书。 用户用隐私政策了解他们的数据被怎么处理,但很多隐私政策写成了"免责声明",晦涩难懂。GDPR 要求"清晰易懂的语言"(Art.12)。

通关挑战

  • 热身:审查你最近参与的一个项目。列出系统中所有存储了用户个人数据的地方(数据库表、日志、缓存、搜索索引、备份)。每个地方有没有对应的留存策略?
  • 挑战:对你当前的项目设计一个完整的数据生命周期管理方案。包括:(1) 数据分类(哪些列是敏感的?)(2) 留存时间表(每种数据保留多久?)(3) 自动清理脚本(用 Python 或 SQL 实现定时清理)(4) 假名化策略(日志和备份中的敏感数据如何处理?)
  • 排障:用户要求删除全部个人数据。你的系统包括:PostgreSQL(主库 + 热备 + 冷备 30 天)、MongoDB(操作日志)、Elasticsearch(全文搜索)、S3(用户上传的文件)、Redis(缓存 24 小时过期)。你的删除计划应该覆盖哪些?怎么处理"数据存了但还没找到它在哪里"的情况?
  • 观察:在开发者工具 > Application > Storage > Cookies 和 Local Storage 中,查看一个网站存储了什么数据。哪些是必要的(session token),哪些可能是不必要的(追踪、分析)。

旅人笔记

  • GDPR 七项原则:以数据最小化和存储限制为核心——少存,明确用途,到时间就删
  • 用户有权请求删除个人数据,系统需要覆盖所有数据副本(生产、备份、日志、缓存)
  • 假名化降低风险(但仍受 GDPR 管辖),匿名化风险更低(但更难真正实现)
  • 差分隐私用噪声保护个体,适合统计场景
  • 数据生命周期管理必须自动化——人工清理迟早会忘
  • 隐私不是法律团队的专属领域——工程决策直接影响隐私保护能力

下一站预告

边境要塞的防线终于搭建完成。你从密码学的基础知识出发,建立了加密通信;通过 PKI 获得了可信的身份验证;用 OAuth 和 OIDC 管理了授权;修复了 web 应用的多个漏洞;在操作系统和网络层面加固了防御;把安全融入了开发流程;并确保用户的数据在隐私层面得到了保护。

握一握你手里的盔甲——它比最初沉重了许多,但每一层都有它的意义。

Built with VitePress | Software Systems Atlas