本章目标:能给数据分级,设计表级/行级/列级权限,写出可运行的脱敏脚本,并说清审计、导出和开发生命周期中的安全边界。 前置知识:数仓分层、用户画像 预计用时:100 分钟
数仓里往往同时有订单金额、手机号、收货地址和身份证号。一旦泄露,损失不是“删库重跑”能弥补的。数据安全不是上线前加一个密码,而是从采集、存储、使用到销毁的整套约束。
1. 为什么需要数据安全
三个现实场景:
- 分析师为了跑数,把生产用户表整表导出到本地 Excel,文件被转发到群里。
- 开发图省事,所有任务共用一个高权限账号,谁改了什么查不到。
- 前端把手机号打码了,但接口仍返回明文,抓包就能拿到。
安全问题的共同点是:便利性压过了边界。数据安全要做的,是把“能访问”变成“按需、可审计、可收回地访问”。
2. 核心概念
2.1 数据分级分类
先给数据定级,再定权限:
| 级别 | 示例 | 默认策略 |
|---|---|---|
| 公开 | 商品类目、城市编码 | 可自由访问 |
| 内部 | 汇总 GMV、订单量 | 员工可申请 |
| 敏感 | 手机号、邮箱、收货地址 | 脱敏后使用,按需审批 |
| 核心 | 身份证号、银行卡、密码 | 禁止明文落数仓,强审计 |
分类还要按业务维度标注,例如用户数据、交易数据、财务数据。分级结果写进元数据,权限系统据此自动套策略。
2.2 权限模型
- RBAC(基于角色):给角色授权,人加入角色。简单、易管理,适合大多数团队。
- ABAC(基于属性):按用户部门、数据标签、访问时间等属性动态判断。灵活,但规则复杂。
落地时通常是组合:角色决定基础权限,属性补充行级和列级限制。
三条原则:
- 最小权限:只给完成工作必需的库表。
- 职责分离:申请权限的人和审批的人分开。
- 权限到期:临时权限设置有效期,到期自动回收。
2.3 脱敏与加密
| 手段 | 做法 | 适用 |
|---|---|---|
| 掩码 | 138****1234 |
展示、客服查询 |
| 哈希 | 加盐 SHA-256 | 需要关联但不需要还原 |
| 加密 | AES 等可逆加密 | 必须还原原文 |
| 令牌化 | 用随机 token 替换原值 | 支付、跨系统传递 |
| 泛化 | 年龄转年龄段、地址到城市 | 统计分析 |
哈希要加盐,防止彩虹表反查;盐值放密钥管理系统,不能写死在代码里。加密密钥和密文分开存放,定期轮换。
2.4 行级与列级权限
- 行级:不同用户看到不同行,例如区域分析师只看本区域订单。
- 列级:同一行里隐藏敏感列,例如都看订单,但不能看手机号。
- 脱敏视图:为敏感表建视图,视图里做掩码或过滤,再把视图权限授给分析师。
原则是在数据出口统一控制,不要让每个查询自己写过滤条件,否则漏一处就泄露。
2.5 审计与生命周期
审计日志至少记录:谁、什么时间、访问了哪张表、执行了什么操作、返回多少行、是否导出。异常行为要告警,例如凌晨大批量导出、短时间扫描全表。
数据生命周期包括采集最小化、存储期限、归档、删除。用户注销或撤回授权后,要在规定时间内清理可识别信息,备份中的副本也要有处理方案。
3. 动手示例
3.1 Python 掩码与加盐哈希
import hashlib
import hmac
import os
SALT = os.environ.get("PII_SALT", "local-demo-salt").encode("utf-8")
def mask_phone(phone):
return phone[:3] + "****" + phone[-4:]
def tokenize(value):
return hmac.new(SALT, value.encode("utf-8"), hashlib.sha256).hexdigest()
print(mask_phone("13800001234"))
print(tokenize("13800001234")[:16])
print(tokenize("13800001234") == tokenize("13800001234"))
输出:
138****1234
0d08e01ea9084f93
True
同一个手机号每次哈希结果相同,可以用于关联;但没有盐无法从哈希还原手机号。生产环境把 PII_SALT 放密钥管理系统,仓库里不要提交真实盐值。
3.2 建脱敏视图
CREATE OR REPLACE TABLE user_contact (
user_id VARCHAR,
phone VARCHAR,
email VARCHAR
);
INSERT INTO user_contact VALUES
('U01', '13800001234', 'alice@example.com'),
('U02', '13900005678', 'bob@example.com');
CREATE OR REPLACE VIEW v_user_contact_masked AS
SELECT
user_id,
CONCAT(SUBSTR(phone, 1, 3), '****', SUBSTR(phone, 8, 4)) AS phone_mask,
CONCAT(SUBSTR(email, 1, 1), '***@', SPLIT_PART(email, '@', 2)) AS email_mask
FROM user_contact;
SELECT * FROM v_user_contact_masked ORDER BY user_id;
分析师只拿到 v_user_contact_masked 的权限,原始表权限留在少数管理员手里。掩码规则要覆盖所有敏感列,新增字段时同步更新视图。
3.3 行级权限
以 PostgreSQL 的行级安全为例:
ALTER TABLE ads_order_wide ENABLE ROW LEVEL SECURITY;
CREATE POLICY p_city ON ads_order_wide
FOR SELECT
USING (city = current_setting('app.current_city', true));
SET app.current_city = '北京';
SELECT * FROM ads_order_wide;
会话变量由应用在建立连接时注入,用户无法在 SQL 里随意修改。表所有者默认绕过行级策略,生产环境要确认是否需要 FORCE ROW LEVEL SECURITY。
其他引擎的写法不同,但模型一致:
Hive/Spark:Ranger 策略按库表列和行过滤条件授权
StarRocks/Doris:ROW POLICY 绑定角色,USING 里写过滤表达式
ClickHouse:行级策略 + 视图 + 列级授权组合
3.4 小样本抑制
统计结果也可能泄露个人信息。例如某个城市只有一个 25 岁用户,按年龄分组后等于直接暴露他。低于阈值的分组要抑制:
SELECT city, age_group, COUNT(*) AS user_cnt
FROM user_profile
GROUP BY city, age_group
HAVING COUNT(*) >= 5;
HAVING COUNT(*) >= 5 只保留至少 5 人的分组。这是最基础的 k-匿名思路,不能替代完整的隐私保护方案,但能挡住明显的个体识别。
3.5 审计查询
SELECT user_name, action, table_name, query_id, event_time
FROM audit_log
WHERE event_time >= TIMESTAMP '2024-03-01 00:00:00'
AND action IN ('SELECT', 'EXPORT')
ORDER BY event_time DESC
LIMIT 20;
审计日志本身也要保护:只读、防篡改、单独存储,保留期限满足合规要求。
4. 常见坑
4.1 明文写日志
调试时把整个请求体打进日志,里面带手机号和地址。日志系统往往权限更宽、保留更久,泄露面更大。日志只记录必要字段,敏感值先脱敏。
4.2 生产数据直接复制到开发
开发环境权限松、缺审计,复制一份生产数据等于扩大泄露面。开发用脱敏样本或合成数据。
4.3 共用高权限账号
所有任务用一个管理员账号,出问题无法定位责任人。每个任务独立账号,按需授权,离职或任务下线及时回收。
4.4 只在界面打码
前端显示 138****1234,接口返回的 JSON 里还是明文。脱敏要在服务端完成,前端拿不到原文。
4.5 哈希不加盐
不加盐的哈希可以被彩虹表反查,手机号空间有限,几小时就能撞出来。使用带盐的 HMAC 或慢哈希,并保护盐值。
4.6 权限只加不减
项目结束、人员转岗后权限还在。建立定期复核机制,临时权限设置过期时间。
4.7 导出文件失控
数据导出成 CSV 后脱离平台管控,被转发、上传网盘。限制导出字段和行数、加水印、记录导出审计,敏感数据默认不允许导出。
4.8 备份成为盲区
主库做了脱敏,备份还是明文;主库删除了数据,备份还保留多年。备份加密、保留策略和删除流程要一起设计。
5. 在线练习
| 题目 | 考点 |
|---|---|
| 1517. 查找拥有有效邮箱的用户 | 邮箱格式校验 |
| 1527. 患某种疾病的患者 | 敏感信息的条件筛选 |
| 196. 删除重复的电子邮箱 | 去重与保留最小主键 |
自测:
- 能写出一条只返回脱敏手机号的视图定义。
- 能解释为什么行级权限不能只在前端实现。
6. 面试延伸
面试题:数据分级怎么做?
是什么:按敏感程度分为公开、内部、敏感、核心,再结合业务分类打标签,写入元数据。
解决什么问题:不同数据需要不同强度的保护,统一分级后可以自动套用权限、脱敏和审计策略。
我的实操边界:本章给出分级表和脱敏示例,未参与企业级数据分类分级项目。
面试题:脱敏和加密有什么区别?
是什么:脱敏不可逆或只保留部分信息,用于展示和分析;加密可逆,用于需要还原原文的传输和存储。
解决什么问题:分析场景不需要原文,脱敏降低泄露风险;需要还原的场景才用加密,并承担密钥管理成本。
我的实操边界:本章写过掩码和加盐哈希,未设计过密钥轮换体系。
面试题:行级权限怎么实现?
是什么:数据库行级安全策略、查询网关注入过滤条件、或给不同角色建脱敏视图。
解决什么问题:让同一张表对不同用户呈现不同数据范围,且无法通过改 SQL 绕过。
我的实操边界:本章在 PostgreSQL 语法下写了策略,未在生产权限平台配置。
面试题:怎么防止数据导出泄露?
是什么:限制可导出字段和行数、敏感列禁止导出、导出走审批和异步任务、加水印并记录审计。
解决什么问题:导出后的文件脱离平台控制,必须在出口处做最后一道限制。
我的实操边界:了解导出管控思路,未实现水印和文件追踪。
7. 本章验收
- 能给出四级数据分类,并为每级举两个字段例子。
- 能写出一个手机号和邮箱的脱敏视图。
- 能运行 Python 加盐哈希示例,说明为什么同一个号码哈希结果相同。
- 能写出 PostgreSQL 的行级权限策略,并说明会话变量由谁注入。
- 能解释小样本抑制要解决什么问题。
- 能列出至少四条数据导出和生命周期管理中的安全要求。