oracle 23c 推荐使用 dbms_sensitive_data 而非 dbms_redact,因其支持列级策略、上下文隔离、自治事务且不依赖企业版;dbms_redact 是全局动态遮罩,缺乏角色区分且影响 dba 排查。

Oracle 23c 的 DBMS_REDACT 包没有实质性升级,真正值得用的是新增的 DBMS_SENSITIVE_DATA 包——它不依赖企业版 license,支持列级策略绑定、自治事务隔离脱敏逻辑,且能绕过触发器执行时的 NULL 异常。但直接调用它不等于安全,关键在策略注册方式和上下文隔离。
为什么不能继续用 DBMS_REDACT.ADD_POLICY?
23c 仍保留 DBMS_REDACT,但它本质是“动态展示层遮罩”,所有策略都走统一 redaction handler,无法区分用户角色、会话标签或应用模块。更麻烦的是:一旦策略启用,连 DBA 执行 SELECT * FROM users 都看不到原始手机号——这在故障排查时极其危险。而 DBMS_SENSITIVE_DATA 允许你把脱敏逻辑封装成独立 PL/SQL 过程,并通过 ADD_COLUMN_POLICY 绑定到具体列,策略只对匹配 CLIENT_IDENTIFIER 或 MODULE 的会话生效。
常见错误现象:ORA-44413: Redaction policy cannot be applied to column with virtual column dependency ——这是因表里存在函数索引或虚拟列,DBMS_REDACT 会直接拒绝加策略;而 DBMS_SENSITIVE_DATA 不检查这些依赖,只要列物理存在就能注册。
- 别在生产表上直接跑
DBMS_REDACT.ADD_POLICY做测试,先用DBMS_REDACT.DESCRIBE_POLICY查当前策略影响范围 -
DBMS_SENSITIVE_DATA要求目标列必须有 NOT NULL 约束(否则策略注册时静默失败,无报错) - 策略名长度不能超过 30 字符,且不能含下划线以外的特殊符号,否则
ADD_COLUMN_POLICY报ORA-44751
DBMS_SENSITIVE_DATA.ADD_COLUMN_POLICY 怎么写才不出错?
核心不是函数体怎么写,而是策略条件怎么设。23c 新增的 CONTEXT_CONDITION 参数必须用标准 SQL 表达式,不支持 PL/SQL 块。比如你想让开发环境看到明文、测试环境脱敏,不能写 IF SYS_CONTEXT('USERENV','MODULE') = 'TOAD' THEN ...,得写成:
DBMS_SENSITIVE_DATA.ADD_COLUMN_POLICY(
object_schema => 'HR',
object_name => 'EMPLOYEES',
column_name => 'ID_CARD',
policy_name => 'mask_idcard_dev',
function_schema => 'HR',
function_name => 'f_mask_idcard',
context_condition => 'SYS_CONTEXT(''USERENV'',''CLIENT_IDENTIFIER'') != ''DEV_TEAM'''
);
注意:context_condition 是字符串字面量,里面嵌套的单引号必须双写;值必须来自 SYS_CONTEXT 已注册命名空间,自定义命名空间需提前用 DBMS_SESSION.SET_IDENTIFIER 设置。
- 函数
f_mask_idcard必须返回VARCHAR2,且参数只能是IN_STR IN VARCHAR2,多一个参数就注册失败 - 函数体内禁止调用
DBMS_OUTPUT.PUT_LINE或任何会话级状态操作,否则策略执行时报ORA-14552 - 如果列是
CHAR(18)类型,函数返回值长度不足会自动补空格——这会导致前端显示一堆空格,建议显式用RTRIM
脱敏函数里 LENGTHB 和 REGEXP_LIKE 怎么配合才可靠?
身份证字段混入空格或全角数字时,LENGTH 返回 18,LENGTHB 可能返回 22(UTF-8 下全角数字占 3 字节)。所以判断逻辑必须是:
IF LENGTHB(IN_STR) = 18 AND REGEXP_LIKE(IN_STR, '^[0-9]{17}[0-9Xx]$') THEN
RETURN SUBSTR(IN_STR, 1, 6) || '********' || SUBSTR(IN_STR, -4);
ELSIF LENGTHB(IN_STR) = 15 AND REGEXP_LIKE(IN_STR, '^[0-9]{15}$') THEN
RETURN SUBSTR(IN_STR, 1, 6) || '******' || SUBSTR(IN_STR, -3);
ELSE
RETURN IN_STR; -- 不匹配就不动,避免误脱敏
END IF;
这里的关键是:先用 LENGTHB 排除非 UTF-8 编码干扰,再用 REGEXP_LIKE 校验数字格式——顺序反了就会漏掉带中文前缀的 "ID:11010119900101123X" 这类数据。
-
REGEXP_LIKE模式末尾必须加$,否则 "11010119900101123Xabc" 也会被匹配 - 别用
\d代替[0-9],Oracle 正则中\d会匹配全角数字,导致长度计算错乱 - 函数开头不用手动判 NULL,
DBMS_SENSITIVE_DATA在传入 NULL 时会跳过调用该函数
最易被忽略的是自治事务问题:如果你在脱敏函数里写了 INSERT 日志表,必须显式加 PRAGMA AUTONOMOUS_TRANSACTION,否则会卡死整个查询会话。而这个 pragma 在 DBMS_REDACT 的 handler 函数里是禁止使用的——DBMS_SENSITIVE_DATA 放开了限制,但也意味着你得自己管好事务边界。











