视图上配不了策略:KingbaseES v9.4.12 动态脱敏落地时踩到的边界
测试环境要接一批外包联调,库里有手机号、金额几列不能给原值。第一版方案很朴素:在基表上建视图,行裁剪、列裁剪都写在视图里,只把视图的 SELECT 权限授给测试账号,一条脱敏策略都不加。推下去第一步就卡住——视图上根本配不了脱敏策略。改成直接在基表上加策略,后面又连着遇到物化视图刷不动、分区表策略加到子分区无效、测试同事用 CTAS 把脱敏后的值固化进自己的中间表。下面按手册条目把这些边界理一遍。
插件与扩展
动态脱敏由 sys_anon 提供,先在 kingbase.conf 里预加载:
shared_preload_libraries = 'sys_anon'
新建的数据库还要单独建扩展:
CREATE EXTENSION sys_anon;
脱敏在 SQL 查询结果返回时才改值,缓存和存储里仍是原值,字段类型和格式不变,所以对应用是透明的;同一个库上的不同连接可以同时处于脱敏和不脱敏状态。
add_policy 的五个参数
add_policy(policy_name text, objname text, username text, func_desc text, para_list text)
policy_name:策略名,不可为空,唯一。objname:待脱敏对象,格式模式.表.列;不写模式时为表.列,模式默认public。user_name:待脱敏对象的用户名,可为空,为空表示对所有用户脱敏。func_desc:脱敏函数名,可为空,为空则走默认脱敏。para_list:函数参数列表,目前仅适用于部分脱敏,参数逗号分隔,例如'2,2'。
三种脱敏函数
默认完整脱敏 default:不指定函数时即为此种。字符类型返回空值;数字类型返回 0(不保证精度);布尔类型返回 f;时间类型年月日部分为 1970-01-01,时分秒为 00:00:00。
test=# CREATE TABLE t1(a int);
test=# \c test sso
test=> SELECT anon.add_policy('pol1', 't1.a', '','','');
部分数据脱敏 partial(prefix int, suffix int):适用于 varchar、char、text。prefix 保留前 n 位,suffix 保留后 n 位,其余位用 * 替换。
SELECT anon.add_policy('pol2','public.t2.a','u1','partial','2,2');
随机数据脱敏:random_string 用于字符类型,变长按实际长度生成随机串,定长按最大长度;random_date 生成 1900-01-01 00:00:00 至今之间的随机时间;random_int 生成 0 至该类型最大值之间的随机整数。
四个容易忽略的细节:
- 部分脱敏要保留的位数超过字符串长度时,退化为最基本的部分脱敏:字符串长度为奇数时脱敏最中间的一个字符,偶数时脱敏最中间的两个字符。
- 字符串类型脱敏,定长按最大长度算,变长按实际长度算。
- 带策略的列改数据类型时,只能改成当前策略支持范围内的类型。
- 要让策略对多个用户生效,添加策略时
username直接置空。
手册条目与现场后果
| 手册中的说明 | 落到操作上的后果 |
|---|---|
| 策略仅允许安全员 sso 配置及查看,其他用户均无权限 | 应用账号、普通 DBA 账号既调不了 anon.add_policy,也看不到已配的策略;建策略、查策略都要换 sso 连接 |
| 仅支持普通表、分区表和物化视图,不支持普通视图 | 视图 + 授权这条隔离路线走不通,视图上没法配策略 |
| 在脱敏表上建视图后,脱敏用户查询视图得到的是脱敏后的值 | 视图可以继续用来看,但脱敏效果来自基表策略,不是视图本身 |
| 策略仅对用户表及物化视图有效,系统表无效 | 系统表上的敏感列不在保护范围 |
| 不支持在脱敏表上建立物化视图;已建物化视图的表上装策略,物化视图将无法更新数据 | 先建的物化视图会卡在刷新这一步 |
| 仅支持在分区表的主表上配置脱敏策略 | 策略挂主表生效,挂到子分区上不起作用 |
user_name 为空则对所有用户脱敏 | 空串不是“不脱敏”,是全库连接都脱敏,运维自己查也是脱敏值 |
| partial 保留位超过字符串长度时,奇数长度脱最中间一个字符,偶数长度脱最中间两个 | 短值不会原样返回,也不会报错,只是中间被替换掉 |
| 定长按最大长度脱敏,变长按实际长度脱敏 | char(n) 列即使只存一个字符也按 n 位处理,输出长度与原值对不上 |
| 带策略的列修改数据类型,只能改为当前策略支持范围内的类型 | 跨类型改列会被挡住,改表结构前要先清理策略 |
CTAS 或 INSERT INTO ... SELECT 的数据来源于脱敏数据 | 落盘的就是脱敏后的值,且无法还原 |
MERGE INTO 的数据来源于脱敏表(无论是否包含脱敏列) | 均无法更新数据 |
| 分区表上存在脱敏策略时交换分区 | 只交换分区数据,不交换脱敏策略 |
| 设置了动态脱敏策略的表,需要谨慎授予其他用户 trigger 权限 | 拿到 trigger 权限的用户可以借触发器绕过脱敏 |
策略可用 sys_dump / sys_restore 备份恢复,需用 sso | 换普通账号做备份恢复会拿不到策略;升级前先备份脱敏配置,升级后恢复 |
可复现的验证过程
-- kingbase.conf: shared_preload_libraries = 'sys_anon'
CREATE EXTENSION sys_anon;
-- 建表并写入两条测试数据
CREATE TABLE t2(a varchar(32), b int);
INSERT INTO t2 VALUES ('13800001234', 100), ('abc', 200);
-- 切到安全员 sso,只对 u1 生效,保留前 2 后 2 位
\c test sso
SELECT anon.add_policy('pol2','public.t2.a','u1','partial','2,2');
-- 换被脱敏用户查询
\c test u1
SELECT * FROM t2;
-- 对照:sso 自己查仍然是原值
\c test sso
SELECT * FROM t2;
按规则推出来的结果:13800001234 共 11 位,保留前 2 后 2,中间 7 位被替换,得到 13*******34;abc 只有 3 位,保留位超过长度,走奇数长度的回退规则,脱掉最中间一个字符,得到 a*c。
要验证 username 置空的效果,换一列再加一条策略,此时所有连接看到的都是默认脱敏值:
SELECT anon.add_policy('pol_all','public.t2.b','','','');
策略的备份恢复同样要 sso 身份:
./sys_dump -d test -p 54321 -U sso -W -F c -f anon.dmp
./sys_restore -p 54321 -U sso -W -d test anon.dmp
排查顺序
shared_preload_libraries里有没有sys_anon,目标库有没有执行过CREATE EXTENSION sys_anon。- 当前连接是不是 sso。非 sso 连策略都看不见,先排除这一层再谈其他。
- 看对象类型:普通表、分区表主表、物化视图可以配;普通视图、系统表不行。
- 看
user_name参数是不是空串。空串等于全用户脱敏,经常被当成关闭开关。 - 用 sso 和目标用户各查一次同一张表,确认差异是策略产生还是别处的视图/中间表带来的。
- partial 结果不对时,先量字符串实际长度,再确认列是定长还是变长。
- 检查脱敏表上是否已经存在物化视图、是否把 trigger 权限授了出去、下游有没有 CTAS 或
MERGE INTO作业。 - 备份恢复与升级流程里,确认
sys_dump/sys_restore用的是 sso,升级前后的脱敏配置备份恢复都跑过。
文档链接:https://bbs.kingbase.com.cn/kingbase-doc/v9.4.12/safety/safety-guide/data-redaction.html