一、背景与合规要求
引言:从逻辑删除到物理销毁的必要性 在数据安全的全生命周期管理中,“数据销毁”是防止敏感信息泄露的最后一道,也是最关键的一道防线。传统的“删除”操作,无论是SQL中的 DELETE 命令还是应用层的“逻辑删除”(通过 is_deleted 等标记位实现),本质上并未将数据从物理存储介质上抹除。这些数据依然残留在数据页中,可能通过磁盘恢复工具、数据库底层文件扫描甚至特定的查询语句被恢复或泄露,构成巨大的安全隐患。 风险根源:操作系统和数据库存储引擎的文件回收机制采用"延迟物理释放"策略,以提高性能。但这导致敏感数据(身份证号、银行卡号、健康记录、生物特征等)在"已删除"状态下仍实际驻留磁盘,违反以下合规要求: • 等保2.0:数据销毁需达到"不可恢复"级别 • GDPR:被遗忘权(Right to be Forgotten)要求数据彻底删除 • 《数据安全法》:数据处理活动需采取必要安全措施
金仓数据库(KingbaseES)深刻理解这一风险,KingbaseES V9R2C014及以上版本引入敏感数据销毁功能,提供了超越常规逻辑删除的物理级数据销毁能力,以满足金融、政务、医疗等核心场景对数据安全的最高合规要求。
二、技术原理深度解析
第一部分:传统“删除”的技术路径与残留风险
1. 逻辑删除(标记位)
o 原理:在数据表中增加状态列(如 is_deleted),执行“删除”时仅更新标记位,而非物理删除记录。 o 数据残留:原始数据的完整字节仍保留在表的数据文件(.dat)中。拥有相应权限的人员可直接读取文件或执行无过滤查询获取全部数据。 o 风险:仅优化查询性能,未改变数据物理存储的事实,不符合“不可恢复”的销毁要求。
2. SQL标准DELETE语句
o 原理:DELETE 语句将行标记为“死元组”(Dead Tuple),其空间被标记为“可复用”。 o 数据残留:“死元组”会保留在数据页中,直到被 VACUUM 进程清理。即使执行 VACUUM,默认情况下也只是标记为“空闲空间”,原始字节未被覆盖。在空间被新数据覆盖前,仍可能通过专业工具恢复。 o 风险:不符合《网络安全法》、《数据安全法》及《个人信息保护法》中对数据销毁需达到“不可恢复”级别的合规要求。
结论:传统删除方式无法满足高等级数据安全合规要求,存在敏感信息泄露的实质性风险。
第二部分:金仓数据库物理级销毁技术原理与实现
金仓数据库的物理级销毁功能,其核心目标是确保指定的敏感数据从物理存储介质上被彻底擦除,使其无法通过任何软硬件手段恢复。
1. 技术原理深度解析
金仓数据库的敏感数据销毁功能采用介质级覆写技术,与动态脱敏、透明加密共同构成数据安全防护体系:
| 安全机制 | 作用时机 | 保护对象 | 最终状态 | 对抗场景 |
|---|---|---|---|---|
| 动态数据脱敏 | SELECT查询时 | 展示层数据 | 内存中展示脱敏结果 | 防屏幕窥探、越权查询 |
| 透明加密(TDE) | 磁盘写入/读取时 | 静态存储数据 | 文件以密文形式存储 | 防磁盘被盗、物理窃取 |
| 敏感数据销毁 | DROP/TRUNCATE时 | 生命周期终结数据 | 0/1 多次复写后释放 | 防数据恢复取证 |
2. KingbaseES物理销毁核心技术
KingbaseES的敏感数据销毁功能采用介质级覆写技术:
• 覆写时机与流程:当被标记为敏感数据的对象(如表、索引)执行 DROP 或 TRUNCATE 操作时,系统不会立即释放空间,而是:
1.先定位到数据所在的物理页面和偏移量。
2.向原占用的内存页和对应的磁盘块反复填写特定的覆写模式。
3.完成指定次数的覆写后,才将空间释放给操作系统。
• 覆写算法(安全等级):支持多种覆写模式,以满足不同安全等级需求:
o 1次覆写:用 0x00 填充所有数据块。快速,满足基本安全要求。
o 3次覆写:0x00 → 0xFF → 随机数据。在安全与性能之间取得平衡。
o 7次覆写:遵循 DoD 5220.22-M 标准(0x00 → 0xFF → 随机 → 0x00 → 0xFF → 随机 → 验证)。提供最高级别的安全性,可抵抗磁力显微镜等高级取证分析。
• 支持的对象类型与标记:
o 需显式标记:普通表、临时表、继承表、分区表、索引、物化视图等数据库对象可以被标记为“敏感”,其销毁时会触发物理覆写。
o 默认销毁:所有临时文件(如排序、哈希临时文件)无需标记,在生命周期结束时默认执行安全销毁。
o 继承规则:对于继承表或分区表,敏感标记具有向下传递性。子表或分区会继承父表的敏感属性,但父表自身的属性不受子表影响。
• 与存储引擎及维护操作集成:
o 该功能与KingbaseES存储引擎深度集成,可绕过部分“延迟清理”优化,直接执行原地覆写。
o 对于需要批量、彻底销毁的需求,物理销毁功能可以与 VACUUM FULL 或表重写操作结合。在重写数据文件时,跳过所有“死元组”空间,并对敏感列进行覆写填充,确保新文件中不包含任何待销毁数据的痕迹。
第三部分:技术优势与合规价值
1.真正不可恢复
从物理介质层面破坏数据二进制信息,满足等保2.0、GDPR(被遗忘权)、《数据安全法》等法规中最高等级的“不可恢复”数据销毁合规要求。
2.精准与灵活
o 精准销毁:可针对特定行、特定列(如身份证号、手机号字段)进行销毁,不影响同行其他数据。
o 性能可控:支持对少量关键记录的实时覆写,也可结合维护窗口对大量历史数据进行批量物理清理。
3.审计强化
销毁操作会生成详细的数据库日志,结合金仓数据库强大的审计功能(参考相关安全指南),可实现销毁操作的全链路可追溯,满足安全审计要求。
4.主动防御
构成了对“数据恢复取证”攻击的有效防御,是数据安全生命周期“终局”环节的关键技术保障。
第四部分:敏感标记分步配置与实操测试
4.1 环境配置准备
4.1.1 确认KingbaseES版本支持敏感数据销毁功能
SELECT version();
预期输出应包含 V9R2C014 或更高版本
4.1.2 调整参数及加载插件
修改kingbase.conf文件:
创建插件:
create extension kdb_sens;
4.1.3 sso设置敏感标记开关
\c - sso
alter system set kdb_sens.enable = on/off;
select sys_reload_conf();
4.1.4 sso设置销毁力度(覆盖次数)
\c - sso
alter system set kdb_sens.erase_force = 'easy/normal/hard';
select sys_reload_conf();
easy: 擦除1次 normal:擦除3次 hard::擦除7次
全局级别设置:
alter system set kdb_sens.erase_force = 7;
select sys_reload_conf();
会话级别设置:本次连接执行7次覆写
set kdb_sens.erase_force =7;
#需要单独授权
4.2 准备测试环境及数据
4.2.1 创建测试数据库和用户
CREATE DATABASE secure_erase_test;
\c secure_erase_test
4.2.2 创建测试角色并授权
CREATE ROLE security_admin WITH LOGIN PASSWORD 'SecurePass123!';
GRANT ALL ON DATABASE secure_erase_test TO security_admin;
4.2.3 本次使用的测试表说明
启用敏感数据标记有三种方式,本次测试涉及的测试表及作用 shanjia_cust1--创建时直接标记为敏感数据 shanjia_cust2--命令行修改已存在的表进行标记为敏感数据 shanjia_cust3--sso调用接口将已存在的表设置为敏感数据表 shanjia_cust4--不标记敏感数据对比表
4.3 开始进行测试及验证
4.3.1 创建测试表时直接标记为敏感数据
\c secure_erase_test
CREATE TABLE shanjia_cust1 (
cust_id BIGSERIAL PRIMARY KEY,
cust_name VARCHAR(50) NOT NULL,
id_card VARCHAR(18) NOT NULL,
phone VARCHAR(11) NOT NULL,
email VARCHAR(100),
bank_card VARCHAR(19),
address TEXT,
register_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login_time TIMESTAMP,
account_balance DECIMAL(15,2) DEFAULT 0.00,
credit_level VARCHAR(10),
is_active BOOLEAN DEFAULT TRUE,
is_deleted BOOLEAN DEFAULT FALSE
) WITH (sensitive_data = true);
验证表标记状态
SELECT relname, reloptions
FROM sys_class
WHERE relname = 'shanjia_cust1';
预期输出:reloptions = '{sensitive_data=true}'
创建索引也会继承敏感属性(如果基于敏感表)
CREATE INDEX idx_shanjia_cust1_id_card ON shanjia_cust1(id_card);
CREATE INDEX idx_shanjia_cust1_phone ON shanjia_cust1(phone);
CREATE INDEX idx_shanjia_cust1_bank_card ON shanjia_cust1(bank_card);
验证索引会正常继承
SELECT
'索引级验证' AS 验证类型,
i.indexrelid::regclass AS 索引名,
c.relname AS 所属表,
c.reloptions AS 表存储参数,
i.indrelid::regclass AS 基表,
CASE
WHEN c.reloptions::text LIKE '%sensitive_data%' THEN '继承敏感属性'
WHEN c.reloptions::text LIKE '%secure_erase%' THEN '继承安全删除属性'
ELSE '未继承敏感属性'
END AS 继承状态,
sys_size_pretty(pg_relation_size(i.indexrelid)) AS 索引大小
FROM sys_index i
JOIN sys_class c ON i.indrelid = c.oid
WHERE c.relname LIKE 'shanjia_cust2'
ORDER BY 所属表, 索引名;
经过上边的测试可以看出来,我们在创建表的时候启用敏感之后,后续创建的索引,也会进行继承敏感属性。
4.3.2 使用命令行方式标记已存在的表
CREATE TABLE shanjia_cust2 (
cust_id BIGSERIAL PRIMARY KEY,
cust_name VARCHAR(50) NOT NULL,
id_card VARCHAR(18) NOT NULL,
phone VARCHAR(11) NOT NULL,
email VARCHAR(100),
bank_card VARCHAR(19),
address TEXT,
register_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login_time TIMESTAMP,
account_balance DECIMAL(15,2) DEFAULT 0.00,
credit_level VARCHAR(10),
is_active BOOLEAN DEFAULT TRUE,
is_deleted BOOLEAN DEFAULT FALSE
);
CREATE INDEX idx_shanjia_cust2_id_card ON shanjia_cust2(id_card);
CREATE INDEX idx_shanjia_cust2_phone ON shanjia_cust2(phone);
CREATE INDEX idx_shanjia_cust2_bank_card ON shanjia_cust2(bank_card);
对已存在的对象启用敏感数据销毁
ALTER TABLE shanjia_cust2 SET (sensitive_data = true);
验证表标记状态
SELECT relname, reloptions
FROM sys_class
WHERE relname = 'shanjia_cust2';
预期输出:reloptions = '{sensitive_data=true}'
验证索引能否正常继承敏感属性
SELECT
'索引级验证' AS 验证类型,
i.indexrelid::regclass AS 索引名,
c.relname AS 所属表,
c.reloptions AS 表存储参数,
i.indrelid::regclass AS 基表,
CASE
WHEN c.reloptions::text LIKE '%sensitive_data%' THEN '继承敏感属性'
WHEN c.reloptions::text LIKE '%secure_erase%' THEN '继承安全删除属性'
ELSE '未继承敏感属性'
END AS 继承状态,
sys_size_pretty(pg_relation_size(i.indexrelid)) AS 索引大小
FROM sys_index i
JOIN sys_class c ON i.indrelid = c.oid
WHERE c.relname LIKE 'shanjia_cust2'
ORDER BY 所属表, 索引名;
经过上边的测试可以看出来,我们对于已存在的表和索引,启用敏感标记时,索引也会自动继承敏感属性。
4.3.3 使用接口方式调用启用敏感数据
CREATE TABLE shanjia_cust3 (
cust_id BIGSERIAL PRIMARY KEY,
cust_name VARCHAR(50) NOT NULL,
id_card VARCHAR(18) NOT NULL,
phone VARCHAR(11) NOT NULL,
email VARCHAR(100),
bank_card VARCHAR(19),
address TEXT,
register_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login_time TIMESTAMP,
account_balance DECIMAL(15,2) DEFAULT 0.00,
credit_level VARCHAR(10),
is_active BOOLEAN DEFAULT TRUE,
is_deleted BOOLEAN DEFAULT FALSE
);
参数说明: sso调用接口将已存在的表设置为敏感数据表: kdb_sens.set_sensitive_data( schema_name name, --模式名 relation_name name, --表名 is_sensitive_data bool --是否是敏感数据 ); 无返回值
执行调用:
\c - sso
SELECT kdb_sens.set_sensitive_data('public', 'shanjia_cust3', true);
验证表标记状态
SELECT relname, reloptions
FROM sys_class
WHERE relname = 'shanjia_cust3';
预期输出:reloptions = '{sensitive_data=true}'
4.3.4 分区表敏感标记验证
分区表不会自动继承父表的 WITH 子句中的扩展属性(如 sensitive_data = true),需要在创建子分区时显式指定,或后续使用 ALTER TABLE 添加。 验证如下:
CREATE TABLE shanjia_cust_partition (
cust_id BIGSERIAL,
cust_name VARCHAR(50),
id_card VARCHAR(18),
phone VARCHAR(11),
register_date DATE
) PARTITION BY RANGE (register_date)
WITH (sensitive_data = true);
创建子分区
CREATE TABLE shanjia_cust_2025q1 PARTITION OF shanjia_cust_partition
FOR VALUES FROM ('2025-01-01') TO ('2025-04-01');
CREATE TABLE shanjia_cust_2025q2 PARTITION OF shanjia_cust_partition
FOR VALUES FROM ('2025-04-01') TO ('2025-07-01');
验证子分区继承情况
SELECT parent.relname AS 父表, child.relname AS 子表, child.reloptions
FROM sys_inherits i
JOIN sys_class parent ON i.inhparent = parent.oid
JOIN sys_class child ON i.inhrelid = child.oid
WHERE parent.relname = 'shanjia_cust_partition';
边已经成功创建了一个分区表,并将父表标记为敏感数据表。但是:子分区没有继承父表的敏感数据设置(reloptions 为空)。 因此我们手工进行设置: 为现有分区添加敏感数据设置
ALTER TABLE shanjia_cust_2025q1 SET (sensitive_data = true);
ALTER TABLE shanjia_cust_2025q2 SET (sensitive_data = true);
还可以在创建时指定:
CREATE TABLE shanjia_cust_2025q3 PARTITION OF shanjia_cust_partition
FOR VALUES FROM ('2025-09-01') TO ('2025-10-01')
WITH (sensitive_data = true);
CREATE TABLE shanjia_cust_2025q4 PARTITION OF shanjia_cust_partition
FOR VALUES FROM ('2025-10-01') TO ('2026-01-01')
WITH (sensitive_data = true);
SELECT parent.relname AS 父表, child.relname AS 子表, child.reloptions
FROM sys_inherits i
JOIN sys_class parent ON i.inhparent = parent.oid
JOIN sys_class child ON i.inhrelid = child.oid
WHERE parent.relname = 'shanjia_cust_partition';
因此对于分区表而言我们需要注意: 1.父表的设置不会自动继承到子分区 2.分区表需要为每个分区单独设置敏感数据属性 3.建议创建新分区时直接指定敏感数据设置
第五部分:敏感标记触发销毁动作实操验证
5.1 触发安全销毁流程
DROP TABLE 敏感数据表 → 触发安全销毁流程: 步骤1:锁定表,防止并发访问 步骤2:读取数据文件内容 步骤3:根据 erase_force 进行多次覆写 - 第一次覆写:全0 - 第二次覆写:全1 - 第三次覆写:随机数据 - ...(根据配置重复) 步骤4:验证覆写结果 步骤5:删除文件系统条目 步骤6:释放磁盘空间
5.2 查询物理文件位置
SELECT
'物理文件检查' AS 检查项,
sys_relation_filepath(oid) AS 文件路径,
CASE
WHEN sys_relation_filepath(oid) IS NOT NULL THEN '物理文件存在'
ELSE '物理文件不存在'
END AS 状态
FROM sys_class
WHERE relname = 'shanjia_cust1'
AND relkind = 'r';
--参数配置: kdb_sens.log_destroy=true##进行销毁时打印debug1级别的日志信息 log_min_message = 'debug1'##控制打印到日志的消息的级别 重启数据库生效。
提前打开数据库日志查看: tail -200f /data/kdb/data/kes_oracle/sys_log/kingbase-2026-03-16_194355.log
5.3 开始进行测试
进行表删除:
进行日志查看
/data/kdb/data/kes_oracle/sys_log/kingbase-2026-03-16_221057.log
5.4 开启监控进行查看文件变化状态
5.5 查看备份文件对比验证擦除校验:
发现文件已经内容已经被擦除覆写了。
5.6 对比表删除查看:
观察日志里面并没有擦除标记和日志,同时文件监控可以看到文件被直接删除了
总结
KingbaseES通过向敏感数据对象占用的内存或占用的物理文件中反复填写0和1,以实现擦除数据的目的。擦除过程自动进行,用户无需干预。7次覆盖交替模式,抗磁力显微镜分析能力更强。不过覆写次数与I/O开销呈线性关系。对于普通业务表退役,建议1-3次即可;仅当涉及绝密数据(如密钥种子、生物特征)时启用7次。切勿在生产高峰期执行大规模销毁任务。
KES支持创建时标记和已创建对象修改标记两种方式,已创建的对象支持由owner或超级用户修改为敏感数据对象。适用于表、索引、物化视图,临时文件无需标记,全部销毁。其中继承表和分区表的敏感属性只向下传递,不向上传递。若shanjia_cust_partition 是分区主表,分区表不会自动继承父表的 WITH 子句中的扩展属性(如 sensitive_data = true),需要在创建子分区时显式指定,或后续使用 ALTER TABLE 添加。对于数据脱敏功能,只需在父表配置策略即可,子分区会自动生效。;但若其为某父表的子表,父表不会被标记。此设计可防止意外扩大销毁范围。
金仓数据库(KingbaseES)的敏感数据物理级销毁功能,通过集成介质级覆写技术,从根本上解决了传统数据库删除操作“删而不毁”的安全痛点。它使组织能够在数据生命周期结束时,以符合最高安全标准的方式彻底消除敏感信息,为构建完整、可信的数据安全防护体系提供了坚实的技术基础。用户应根据实际业务的安全等级要求,选择合适的覆写算法,并合理规划数据对象的敏感标记策略。
PS:有兴趣的可以联系我,获取对应的文件监控脚本和文件对比脚本。