核心问题
系统是否真的需要这些数据?
隐私不是“不要泄露数据”这么简单。
更根本的问题是:
不该收集的数据,最好一开始就不要进入系统。
数据一旦被收集,就会产生责任:
- 谁能看?
- 存多久?
- 怎么删除?
- 是否会被导出?
- 是否会出现在日志里?
- 是否会进入分析系统?
- 是否会被第三方处理?
- 泄露时影响多大?
所以隐私工程的第一原则是数据最小化:
只收集完成明确目的所必需的数据。
数据不是免费资产
很多团队会把数据当成资产:
先收着,以后可能有用。
这句话非常危险。
因为每一份数据也是负债:
- 存储成本
- 合规成本
- 安全成本
- 访问控制成本
- 删除成本
- 泄露风险
- 用户信任成本
尤其是个人数据。
如果你收集了用户生日、身份证、位置、学习记录、支付记录、聊天内容,就必须承担保护这些数据的责任。
核心句:
不必要的数据不是资产,是风险。
PII:个人可识别信息
PII 是 Personally Identifiable Information,个人可识别信息。
常见 PII:
- 姓名
- 邮箱
- 手机号
- 身份证号
- 地址
- IP 地址
- 支付信息
- 精确位置
- 头像
- 设备标识
还有一些数据单独看不一定能识别个人,但组合起来可以:
城市 + 公司 + 职位 + 学习记录
所以判断 PII 不要只看单个字段,而要问:
这些数据组合后能不能指向一个具体的人?
收集前先问目的
每新增一个字段,先问:
- 我们为什么需要它?
- 它服务哪个用户功能或法律义务?
- 没有它能不能完成核心流程?
- 是否可以用更粗粒度的数据代替?
- 是否可以只临时使用,不持久化?
- 谁会访问它?
- 多久后应该删除?
例如课程平台想收集生日。
可能理由:
为了个性化推荐。
继续追问:
- 推荐真的需要完整生日吗?
- 年龄段是否足够?
- 是否可以由用户自愿填写?
- 是否可以不存原始生日,只存 age bracket?
更小的数据通常更安全:
birthDate -> ageRange
preciseLocation -> countryOrRegion
fullName -> displayName
rawEventPayload -> selectedFields
日志里的隐私
日志是隐私泄露高发区。
坏日志:
logger.info("payment_request", {
email,
phone,
cardNumber,
billingAddress,
rawRequestBody,
})
问题是日志经常被更多系统读取:
- 日志平台
- 监控系统
- 第三方 observability 服务
- 开发人员
- 客服工具
- 离线分析
一旦 PII 进了日志,删除和控制会变得非常难。
更好:
logger.info("payment_request_created", {
accountId,
paymentId,
amount,
currency,
requestId,
})
必要时脱敏:
logger.info("login_attempt", {
emailHash,
phoneLast4,
requestId,
result,
})
原则:
日志应该记录排查所需证据,不记录不必要的私人信息。
数据保留策略
隐私不只是收集,也包括保留。
一个系统应该回答:
数据什么时候应该被删除?
例如:
| 数据 | 保留策略 |
|---|---|
| 登录 session | 过期后删除 |
| 验证码 | 几分钟后删除 |
| 原始 webhook payload | 保留有限天数 |
| 审计日志 | 按合规要求保留 |
| 支付记录 | 按财务和法律要求保留 |
| 临时导出文件 | 几小时或几天后删除 |
没有保留策略的数据,会自然变成永久数据。
核心句:
如果没有删除时间,数据会默认永生。
删除权和可删除性
很多系统说支持删除用户,但实际做不到。
因为用户数据散落在:
- 主数据库
- 日志
- 搜索索引
- 缓存
- 分析仓库
- 邮件系统
- 第三方服务
- 备份
所以设计数据时,要考虑:
如果用户要求删除,我们能删除到什么程度?
删除通常分几类:
1. 软删除
deletedAt: Date
适合业务需要保留历史关系的对象。
2. 匿名化
保留行为统计,但移除可识别身份。
accountId -> anonymousUserId
email -> null
name -> "Deleted User"
3. 硬删除
彻底删除数据。
适合临时数据、验证码、草稿、非合规必需数据。
4. 法律保留
有些数据因为财务、税务、风控、审计要求不能立即删除。
这时要清楚告知范围,并限制访问。
访问审计
敏感数据不仅要保护写入,也要保护读取。
例如后台查看用户个人信息,应留下审计:
type DataAccessAudit = {
actorId: string
dataSubjectId: string
dataType: "profile" | "payment" | "learning_history"
purpose: string
occurredAt: Date
requestId: string
}
尤其是:
- 查看支付信息
- 导出用户列表
- 查看学习记录
- 查看私密内容
- 客服查询用户账号
原则:
敏感数据的读取也应该被看见。
数据脱敏和分级
不是所有数据风险一样。
可以把数据分级:
Public: 公开课程标题、公开讲师简介
Internal: 账号 ID、课程进度统计
Sensitive: 邮箱、手机号、学习记录、支付记录
Highly Sensitive: 身份证、精确地址、支付卡信息
不同级别对应不同处理:
- 是否可进日志
- 是否可导出
- 是否需要脱敏
- 是否需要审批
- 是否需要加密
- 是否需要访问审计
例如客服后台可以默认显示:
le***@example.com
phone: ***1234
只有在明确业务需要时,才允许查看完整值,并留下审计。
第三方共享
一旦数据发给第三方,你就失去一部分控制。
例如:
- 支付平台
- 邮件服务
- 短信服务
- 分析工具
- 客服系统
- 错误监控
- AI 服务
发送前要问:
- 第三方是否真的需要这个字段?
- 是否可以发内部 ID 而不是 PII?
- 是否可以脱敏或哈希?
- 第三方保留多久?
- 用户删除时,第三方如何删除?
- 是否会跨境传输?
- 是否进入第三方训练或分析?
原则:
数据离开系统边界前,要重新证明必要性。
隐私默认值
好的系统默认保护用户。
例如:
- 默认不公开个人资料
- 默认不展示学习记录
- 默认不订阅营销邮件
- 默认不允许被搜索
- 默认不共享给第三方
- 默认不把敏感字段写入日志
不要把隐私保护变成用户必须自己找到的开关。
核心句:
隐私应该是默认值,不是高级设置。
隐私检查清单
新增一个字段或数据流时问:
- 这个数据是否必要?
- 它服务哪个明确目的?
- 是否可以用更少、更粗、更短期的数据替代?
- 是否是 PII 或敏感数据?
- 谁可以读?谁可以写?
- 是否会进入日志、缓存、分析、搜索或第三方?
- 保留多久?
- 如何删除或匿名化?
- 是否需要脱敏、加密或访问审计?
- 用户是否合理预期我们会这样使用它?
小结
- 隐私的第一原则是数据最小化。
- 不必要的数据不是资产,是风险。
- PII 要看组合识别能力,不只看单个字段。
- 日志是隐私泄露高发区。
- 没有删除时间的数据会默认永生。
- 敏感数据的读取也应该被审计。
- 数据离开系统边界前,要重新证明必要性。
- 隐私应该是默认值,不是高级设置。