数据分级标签设计与存储

标签这东西,说白了就是给数据贴个“身份牌”。你想想看,权限系统要控制谁可以访问什么数据,总得有个依据吧?这个依据就是标签。我早年在做一个政务云项目时,就因为标签设计得太糙,后期权限模型改了三轮,差点没被运维同事骂死。

所以这一章,咱们就聊聊标签到底该怎么设计、怎么存、怎么管。内容分三块:标签结构、嵌入方式、生命周期管理。

标签结构设计:等级、类别、访问范围

我个人习惯把标签结构拆成三个维度:等级、类别、访问范围。这样设计的原因,说起来很简单——你既要知道数据有多敏感,也得知道它属于什么领域,还得知道谁能看。

标签三元组示例: {等级: L3, 类别: 财务, 范围: 内部}

这表示:数据敏感度较高(L3),属于财务类别,只允许内部员工访问。

等级(Classification Level)

等级是标签的核心,直接决定加密强度和访问门槛。我建议至少分四到五级,太少了粒度不够,太多了管理成本爆炸。

等级 名称 典型数据 推荐加密算法
L0 公开 产品介绍、新闻稿 不加密或对称加密
L1 内部 内部通知、组织架构 SM4 / AES-128
L2 敏感 财务报表、员工薪资 SM4 / AES-256
L3 机密 客户隐私、交易记录 SM4 + 密钥分割
L4 绝密 核心算法、密钥材料 专用硬件加密

我曾经踩过这个坑: 把等级直接映射到加密算法强度,结果 L0 和 L1 用了同一种加密,审计时被质疑控制不足。后来我强制要求 L0 不加密,L1 以上才加密,逻辑就清晰多了。

类别(Category)

类别解决的是“数据属于哪个域”。比如财务、人事、研发、销售。同一等级下,不同类别的人可能权限不同。我建议类别用预定义枚举,别让业务方自己随便填,否则分分钟冒出几十个奇怪的类别名。

举个例子。一个财务部的L2数据,人事部的人就不能碰,哪怕他的等级够高。这就是类别在起作用。

访问范围(Scope)

范围定义了数据的“可见边界”。常见取值有:

  • 个人 —— 仅数据所有者可访问
  • 部门 —— 所属部门全员可访问
  • 项目组 —— 项目成员可访问
  • —— 全员可访问(通常仅限L0/L1)

这三个维度组合起来,就能描述绝大多数场景。我个人习惯在代码里用一个结构体来定义:

struct DataLabel {
    uint8_t level;      // 0-4
    uint16_t category;  // 枚举值
    uint8_t scope;      // 0-3
    uint64_t timestamp; // 标签生成时间
};

标签嵌入方式:元数据、数据库字段、文件头

标签设计好了,接下来得考虑怎么跟数据绑在一起。嗯,这里要注意:不同的存储形态,嵌入方式完全不一样。我在项目中主要用过三种方式。

方式一:元数据(Metadata)

适用于对象存储、云存储、HDFS这类系统。标签放在文件的扩展属性或标签字段里,不修改数据本身。

# 给对象存储的文件打标签
PUT /bucket/user_data.csv
X-Amz-Tagging: level=3&category=finance&scope=internal

优点: 对数据零侵入,修改标签不用动文件。
缺点: 如果存储系统不支持元数据,就没法用了。

方式二:数据库字段(Database Column)

适用于关系型数据库。在每张表里加一个data_label字段,或者单独建一张标签关联表。

-- 方案A:直接加字段
ALTER TABLE user_info ADD COLUMN data_label VARCHAR(64);

-- 方案B:关联表(推荐用于多标签场景)
CREATE TABLE data_label_map (
    table_name VARCHAR(64),
    row_id     BIGINT,
    label_json VARCHAR(256)
);

我个人比较推荐方案B,因为标签可能跟随业务变化。方案A改起来太费劲,我之前一个项目就是用的方案A,后来加了个类别维度,跑了48小时做数据迁移,差点被运维点名批评。

小技巧: 标签字段建议使用 JSON 或 Protobuf 序列化存储,便于扩展。但注意,JSON 查询性能稍微差一点,高频访问场景建议用 INT 位掩码做等级和类别。

方式三:文件头(File Header)

适用于二进制文件、加密文件、压缩包。标签嵌入在文件头部的自定义字段里,通常配合加密一起用。

+----------------+----------------+----------------+
| 文件魔数(4B)   | 文件版本(1B)   | 加密标识(1B)   |
+----------------+----------------+----------------+
| 等级(1B)       | 类别(2B)       | 范围(1B)       |
+----------------+----------------+----------------+
| 保留字段(8B)   | 数据内容...    |                |
+----------------+----------------+----------------+

这种方式的好处是标签和文件绑死,拷贝、传输都不会丢。坏处是改标签得重写整个文件。

标签生命周期管理

标签不是贴上去就完事了。数据会变老,敏感度会下降,访问范围也会调整。没有生命周期管理,标签就会变成一堆垃圾数据。

我总结了一个简单的三阶段模型:

  1. 创建期 —— 数据生成时首次打标签。通常由数据生产者或自动分类引擎完成。
  2. 维护期 —— 数据生命周期内,标签可能被复审、升级或降级。我建议设置一个标签复审周期,比如L3数据每半年复审一次,L2每一年。
  3. 废弃期 —— 数据删除或归档时,标签必须同步清理。否则审计时会有“幽灵标签”,看着吓人。

标签变更流程(简化版):

  1. 数据负责人发起标签变更申请
  2. 安全审计组审批(自动/人工)
  3. 更新标签存储(元数据/字段/文件头)
  4. 如果是降级,可能需要先解密再用新密钥重新加密
  5. 记录变更日志,保留旧标签快照(至少180天)

标签快照这事很有意思。有一次审计查半年前的数据,非要看当时的标签是什么。还好我们做了标签历史表,否则就得靠记忆了。

-- 标签历史表结构示例
CREATE TABLE label_history (
    id          BIGINT AUTO_INCREMENT PRIMARY KEY,
    data_id     VARCHAR(128),      -- 数据ID
    old_label   VARCHAR(256),      -- 旧标签JSON
    new_label   VARCHAR(256),      -- 新标签JSON
    operator    VARCHAR(64),       -- 操作人
    change_time DATETIME,          -- 变更时间
    reason      VARCHAR(512)       -- 变更原因
);

最后说一点。标签的生命周期必须和数据的生命周期对齐。数据销毁了但标签还在,那就是安全隐患。我在一个系统里见过100万条记录的数据表只剩10万条数据,但标签关联表里还有80万条记录——这就是典型的“标签泄漏”。

数据分级标签设计全景图 数据分级标签设计全景图 等级(Level) 类别(Category) 范围(Scope) 组合 → 数据标签(三元组) 元数据(Metadata) 对象存储 / HDFS 数据库字段(Column) SQL / KV 存储 文件头(Header) 加密文件 / 二进制 生命周期:创建期 → 维护期(复审/升级/降级) → 废弃期(同步清理)

个人经验分享: 标签的生命周期千万别跟数据的生命周期完全解耦。我在一个项目中吃过亏——数据删除了,但标签还在,结果安全扫描时被当作“未授权数据残留”报告,白挨了一顿批评。后来我强制要求标签表和数据表用同一个主键,删除时级联删除标签,问题才彻底解决。