跳过正文

Oracle 与达梦数据库动态数据脱敏实战:基于 DBMS_REDACT / DBMS_RLS

Greatfinish
作者
Greatfinish
记录 Oracle、PostgreSQL、达梦、Linux、存储与生产环境故障处理经验。
目录

一、前言
#

在企业数据库安全治理中,身份证号、手机号、工资、银行卡号、邮箱、地址等字段通常属于敏感数据。传统做法包括修改真实数据、建立脱敏副本库、应用层代码脱敏、导出后手工处理等,但这些方式通常存在业务侵入大、运维复杂、数据冗余、安全边界不清晰等问题。

动态数据脱敏的核心思想是:真实数据不变,仅在查询结果返回时根据策略动态屏蔽敏感字段

本文结合 Oracle 与达梦数据库两种实现方式,通过员工表中的身份证号、手机号、工资三个字段进行完整演示,并把实际排错过程中遇到的问题一并整理出来,形成一份可复现、可验证、可发布的技术实践文档。

数据库实现方式核心对象
OracleDBMS_REDACTRedaction Policy
达梦 DM8DBMS_RLS + POLICY + MASKRLS Policy + Mask Function

二、Oracle 动态数据脱敏
#

2.1 DBMS_REDACT 理论说明
#

Oracle DBMS_REDACT 是用于实现 Data Redaction 的 PL/SQL 包。其作用是在 SQL 查询结果返回给低权限用户或应用前,对指定列进行实时掩码处理。该机制不修改表中的真实数据,而是在结果返回阶段进行动态处理。

Oracle Data Redaction 的几个关键点:

项目说明
是否修改原始数据不修改
生效阶段查询结果返回前
控制对象表或视图中的敏感列
条件控制通过 expression 判断是否脱敏
绕过机制具有 EXEMPT REDACTION POLICY 权限的用户不会被脱敏
多列脱敏同一对象先 ADD_POLICY,后续列通过 ALTER_POLICY ADD_COLUMN 添加

Oracle 的 expression 逻辑与达梦后面示例不同:

Oracle DBMS_REDACT:expression 为 TRUE 时执行脱敏。

例如:

EXPRESSION => '1=1'

表示对符合条件的普通查询用户执行脱敏。

2.2 创建测试用户
#

CREATE USER HR_TEST IDENTIFIED BY "Oracle123";

GRANT CONNECT, RESOURCE TO HR_TEST;


SQL> CREATE USER HR_TEST IDENTIFIED BY "Oracle123";

User created.

SQL> GRANT CONNECT,RESOURCE TO HR_TEST;

Grant succeeded.

SQL> 

2.3 创建测试表
#

CREATE TABLE HR_TEST.EMP_INFO
(
    EMP_ID     INT,
    EMP_NAME   VARCHAR2(50),
    ID_CARD    VARCHAR2(18),
    MOBILE     VARCHAR2(11),
    SALARY     NUMBER(10,2)
);

插入测试数据:

INSERT INTO HR_TEST.EMP_INFO VALUES
(1, '张三', '310101199001011234', '13812345678', 25000);

INSERT INTO HR_TEST.EMP_INFO VALUES
(2, '李四', '320102198805056789', '13987654321', 18000);

COMMIT;

结果如下:

SQL> CREATE TABLE HR_TEST.EMP_INFO
(
    EMP_ID     INT,
  2    3    4      EMP_NAME   VARCHAR2(50),
    ID_CARD    VARCHAR2(18),
    MOBILE     VARCHAR2(11),
    SALARY     NUMBER(10,2)
);

INSERT INTO HR_TEST.EMP_INFO VALUES
(1, '张三', '310101199001011234', '13812345678', 25000);

INSERT INTO HR_TEST.EMP_INFO VALUES
(2, '李四', '320102198805056789', '13987654321', 18000);

COMMIT;  5    6    7    8
Table created.

SQL> SQL>   2
1 row created.

SQL> SQL>   2
1 row created.

SQL> SQL>

Commit complete.

SQL> select * from HR_TEST.EMP_INFO;

    EMP_ID EMP_NAME                                           ID_CARD
---------- -------------------------------------------------- ------------------
MOBILE          SALARY
----------- ----------
         1 张三                                       310101199001011234
13812345678      25000

         2 李四                                       320102198805056789
13987654321      18000


SQL> 

2.4 Oracle 创建身份证脱敏策略
#

目标:

310101199001011234

310101********1234

添加策略:

BEGIN
  DBMS_REDACT.ADD_POLICY(
    OBJECT_SCHEMA       => 'HR_TEST',
    OBJECT_NAME         => 'EMP_INFO',
    COLUMN_NAME         => 'ID_CARD',
    POLICY_NAME         => 'REDACT_EMP_ID_CARD',
    FUNCTION_TYPE       => DBMS_REDACT.PARTIAL,
    FUNCTION_PARAMETERS => 'VVVVVVVVVVVVVVVVVV,VVVVVVVVVVVVVVVVVV,*,7,14',
    EXPRESSION          => '1=1'
  );
END;
/


SQL> BEGIN
  DBMS_REDACT.ADD_POLICY(
    OBJECT_SCHEMA       => 'HR_TEST',
    OBJECT_NAME         => 'EMP_INFO',
    COLUMN_NAME         => 'ID_CARD',
    POLICY_NAME         => 'REDACT_EMP_ID_CARD',
    FUNCTION_TYPE       => DBMS_REDACT.PARTIAL,
    FUNCTION_PARAMETERS => 'VVVVVVVVVVVVVVVVVV,VVVVVVVVVVVVVVVVVV,*,7,14',
    EXPRESSION          => '1=1'
  );
END;
/  2    3    4    5    6    7    8    9   10   11   12

PL/SQL procedure successfully completed.

SQL> 

2.5 Oracle 添加手机号脱敏
#

目标:

13812345678

138****5678

使用 DBMS_REDACT.ALTER_POLICY,在已有策略中增加列::

BEGIN
  DBMS_REDACT.ALTER_POLICY(
    OBJECT_SCHEMA       => 'HR_TEST',
    OBJECT_NAME         => 'EMP_INFO',
    POLICY_NAME         => 'REDACT_EMP_ID_CARD',
    ACTION              => DBMS_REDACT.ADD_COLUMN,
    COLUMN_NAME         => 'MOBILE',
    FUNCTION_TYPE       => DBMS_REDACT.PARTIAL,
    FUNCTION_PARAMETERS => 'VVVVVVVVVVV,VVVVVVVVVVV,*,4,7',
    EXPRESSION          => '1=1'
  );
END;
/

SQL> BEGIN
  2    DBMS_REDACT.ALTER_POLICY(
    OBJECT_SCHEMA       => 'HR_TEST',
    OBJECT_NAME         => 'EMP_INFO',
    POLICY_NAME         => 'REDACT_EMP_ID_CARD',
    ACTION              => DBMS_REDACT.ADD_COLUMN,
    COLUMN_NAME         => 'MOBILE',
    FUNCTION_TYPE       => DBMS_REDACT.PARTIAL,
    FUNCTION_PARAMETERS => 'VVVVVVVVVVV,VVVVVVVVVVV,*,4,7',
    EXPRESSION          => '1=1'
  );
END;
/  3    4    5    6    7    8    9   10   11   12   13

PL/SQL procedure successfully completed.

SQL> 

特别注意:同一张表只能 ADD_POLICY 一次。后续字段必须:ALTER_POLICY + ADD_COLUMN

2.6 Oracle 添加工资脱敏
#

BEGIN
  DBMS_REDACT.ALTER_POLICY(
    OBJECT_SCHEMA => 'HR_TEST',
    OBJECT_NAME   => 'EMP_INFO',
    POLICY_NAME   => 'REDACT_EMP_ID_CARD',
    ACTION        => DBMS_REDACT.ADD_COLUMN,
    COLUMN_NAME   => 'SALARY',
    FUNCTION_TYPE => DBMS_REDACT.FULL,
    EXPRESSION    => '1=1'
  );
END;
/


SQL> BEGIN
  DBMS_REDACT.ALTER_POLICY(
    OBJECT_SCHEMA => 'HR_TEST',
  2    3    4      OBJECT_NAME   => 'EMP_INFO',
    POLICY_NAME   => 'REDACT_EMP_ID_CARD',
    ACTION        => DBMS_REDACT.ADD_COLUMN,
    COLUMN_NAME   => 'SALARY',
    FUNCTION_TYPE => DBMS_REDACT.FULL,
    EXPRESSION    => '1=1'
  );
END;
/  5    6    7    8    9   10   11   12

PL/SQL procedure successfully completed.

SQL> 

2.7 Oracle 验证脱敏效果
#

CONN HR_TEST/Oracle123

SELECT EMP_ID,EMP_NAME,ID_CARD,MOBILE,SALARY FROM HR_TEST.EMP_INFO;

结果:

SQL> CONN HR_TEST/Oracle123
Connected.

SQL> SET LINES 200
SQL> SELECT EMP_ID,EMP_NAME,ID_CARD,MOBILE,SALARY FROM HR_TEST.EMP_INFO;

    EMP_ID EMP_NAME                                           ID_CARD            MOBILE          SALARY
---------- -------------------------------------------------- ------------------ ----------- ----------
         1 张三                                       310101********1234 138****5678          0
         2 李四                                       320102********6789 139****4321          0

SQL>

2.8 Oracle 查看脱敏策略
#

查看 Policy
#

SELECT OBJECT_OWNER,
       OBJECT_NAME,
       POLICY_NAME,
       EXPRESSION,
       ENABLE
FROM REDACTION_POLICIES
WHERE OBJECT_OWNER = 'HR_TEST'
AND OBJECT_NAME = 'EMP_INFO';

SQL> SELECT OBJECT_OWNER,
       OBJECT_NAME,
       POLICY_NAME,
       EXPRESSION,
       ENABLE
FROM REDACTION_POLICIES
WHERE OBJECT_OWNER = 'HR_TEST'
AND OBJECT_NAME = 'EMP_INFO';  2    3    4    5    6    7    8

OBJECT_OWNER         OBJECT_NAME                    POLICY_NAME                    EXPRESSION           ENABLE
-------------------- ------------------------------ ------------------------------ -------------------- -------
HR_TEST              EMP_INFO                       REDACT_EMP_ID_CARD             1=1                  YES

SQL>

查看脱敏字段
#

SELECT OBJECT_OWNER,
       OBJECT_NAME,
       COLUMN_NAME,
       FUNCTION_TYPE,
       FUNCTION_PARAMETERS
FROM REDACTION_COLUMNS
WHERE OBJECT_OWNER = 'HR_TEST'
AND OBJECT_NAME = 'EMP_INFO';

SQL> SELECT OBJECT_OWNER,
       OBJECT_NAME,
       COLUMN_NAME,
       FUNCTION_TYPE,
       FUNCTION_PARAMETERS
FROM REDACTION_COLUMNS
WHERE OBJECT_OWNER = 'HR_TEST'
AND OBJECT_NAME = 'EMP_INFO';  2    3    4    5    6    7    8

OBJECT_OWNER         OBJECT_NAME                    COLUMN_NAME                    FUNCTION_TYPE        FUNCTION_PARAMETERS
-------------------- ------------------------------ ------------------------------ -------------------- ------------------------------------------------------------
HR_TEST              EMP_INFO                       ID_CARD                        PARTIAL REDACTION    VVVVVVVVVVVVVVVVVV,VVVVVVVVVVVVVVVVVV,*,7,14
HR_TEST              EMP_INFO                       MOBILE                         PARTIAL REDACTION    VVVVVVVVVVV,VVVVVVVVVVV,*,4,7
HR_TEST              EMP_INFO                       SALARY                         FULL REDACTION

SQL>

2.9 Oracle 管理员默认不脱敏
#

数据库管理员保留权限机制。实验验证SYSDBA 默认绕过脱敏策略:

SQL> CONN / AS SYSDBA
Connected.

SQL> SELECT EMP_ID,EMP_NAME,ID_CARD,MOBILE,SALARY FROM HR_TEST.EMP_INFO;

    EMP_ID EMP_NAME                                           ID_CARD            MOBILE          SALARY
---------- -------------------------------------------------- ------------------ ----------- ----------
         1 张三                                       310101199001011234 13812345678      25000
         2 李四                                       320102198805056789 13987654321      18000

SQL>
用户是否脱敏
APP_USER脱敏
SYSDBA不脱敏

三、达梦数据库动态数据脱敏
#

3.1 DBMS_RLS 理论说明
#

达梦数据库提供 DBMS_RLS 包,通过策略 POLICY 管理实现数据行级安全访问控制,即 Row Level Security,简称 RLS。RLS 的原始设计目标是控制不同行或不同机构的数据访问范围,避免在大量 SQL、视图或应用代码中反复手工追加 WHERE 条件。

在脱敏场景中,可以结合以下对象实现动态数据脱敏:

组件作用
ENABLE_RLS开启 RLS 能力
POLICY_FUNCTION返回策略谓词
DBMS_RLS.ADD_POLICY将策略绑定到表或视图
SEC_RELEVANT_COLS指定敏感列
SEC_RELEVANT_COLS_OPT => DBMS_RLS.ALL_ROWS保留所有行,仅处理敏感列显示
DBMS_RLS.ADD_MASK为敏感列绑定掩码函数
MASK FUNCTION定义字段最终显示形式

达梦官方文档明确说明:DBMS_RLS 依赖 ENABLE_RLS=1,开启后需要初始化 DBMS_RLS 系统包,策略对 SYSDBA 用户不会生效,DM MPP 环境不支持 DBMS_RLS 包。

3.2 开启 ENABLE_RLS
#

修改ENABLE_RLS参数,重启数据库实例。

ALTER SYSTEM SET 'ENABLE_RLS' = 1 SPFILE;
EXIT;
DmServiceDMSERVER restart


[dmdba@dameng-srv ~]$ disql SYSDBA
password:

Server[LOCALHOST:5236]:mode is normal, state is open
login used time : 4.068(ms)
disql V8
SQL> ALTER SYSTEM SET 'ENABLE_RLS' = 1 SPFILE;
DMSQL executed successfully
used time: 9.649(ms). Execute id is 6801.
SQL> EXIT
[dmdba@dameng-srv ~]$ DmServiceDMSERVER restart
Stopping DmServiceDMSERVER:                                [ OK ]
Starting DmServiceDMSERVER:                                [ OK ]
[dmdba@dameng-srv ~]$

验证:

SELECT PARA_NAME, PARA_VALUE
FROM V$DM_INI
WHERE PARA_NAME = 'ENABLE_RLS';

应看到:

[dmdba@dameng-srv ~]$ disql SYSDBA
password:

Server[LOCALHOST:5236]:mode is normal, state is open
login used time : 4.601(ms)
disql V8
SQL> SELECT PARA_NAME, PARA_VALUE
FROM V$DM_INI
WHERE PARA_NAME = 'ENABLE_RLS';2   3

LINEID     PARA_NAME  PARA_VALUE
---------- ---------- ----------
1          ENABLE_RLS 1

used time: 7.147(ms). Execute id is 601.
SQL>

3.3 初始化 DBMS_RLS(部分环境需要)
#

部分版本需要初始化:

SP_INIT_RLS_SYS(1);

兼容旧版本的方式:

SP_CREATE_SYSTEM_PACKAGES(1, 'DBMS_RLS');

实际执行时,如果当前版本已经内置或已经初始化,则不需要重复执行。

3.4 创建测试用户
#

CREATE USER HR_TEST IDENTIFIED BY "Dameng123";
GRANT RESOURCE,PUBLIC TO HR_TEST;


SQL> CREATE USER HR_TEST IDENTIFIED BY "Dameng123";
executed successfully
used time: 4.078(ms). Execute id is 608.
SQL> GRANT RESOURCE,PUBLIC TO HR_TEST;
executed successfully
used time: 3.900(ms). Execute id is 609.
SQL>

注意:测试脱敏时不要给业务用户授予 DBA,避免高权限导致判断混乱。

3.5 创建测试表
#

CREATE TABLE HR_TEST.EMP_INFO
(
    EMP_ID     INT,
    EMP_NAME   VARCHAR2(50),
    ID_CARD    VARCHAR2(18),
    MOBILE     VARCHAR2(11),
    SALARY     NUMBER(10,2)
);

插入数据:

INSERT INTO HR_TEST.EMP_INFO VALUES
(1, '张三', '310101199001011234', '13812345678', 25000);

INSERT INTO HR_TEST.EMP_INFO VALUES
(2, '李四', '320102198805056789', '13987654321', 18000);

COMMIT;

执行结果:

SQL> CREATE TABLE HR_TEST.EMP_INFO
(
    EMP_ID     INT,
    EMP_NAME   VARCHAR2(50),
    ID_CARD    VARCHAR2(18),
    MOBILE     VARCHAR2(11),
    SALARY     NUMBER(10,2)
);2   3   4   5   6   7   8
executed successfully
used time: 4.899(ms). Execute id is 610.
SQL> INSERT INTO HR_TEST.EMP_INFO VALUES
(1, '', '310101199001011234', '13812345678', 25000);

INSERT INTO HR_TEST.EMP_INFO VALUES
(2, '', '320102198805056789', '13987654321', 18000);

COMMIT;2   affect rows 1

used time: 3.683(ms). Execute id is 611.
SQL> SQL> 2   affect rows 1

used time: 1.163(ms). Execute id is 612.
SQL> SQL>
executed successfully
used time: 1.035(ms). Execute id is 613.

3.6 创建策略函数
#

达梦策略函数必须符合固定模板:两个输入参数,返回字符串。返回值会作为策略谓词参与访问控制。

CREATE OR REPLACE FUNCTION HR_TEST.RLS_EMP_MASK_FUN
(
    P_OWNER VARCHAR2,
    P_OBJ   VARCHAR2
)
RETURN VARCHAR2
AS
BEGIN
    RETURN '1=2';
END;
/


SQL> CREATE OR REPLACE FUNCTION HR_TEST.RLS_EMP_MASK_FUN
(
    P_OWNER VARCHAR2,
    P_OBJ   VARCHAR2
)
RETURN VARCHAR2
AS
BEGIN
    RETURN '1=2';
END;
/2   3   4   5   6   7   8   9   10  11
executed successfully
used time: 8.267(ms). Execute id is 1202.
SQL>

注意RETURN '1=2';条件不成立,策略生效,触发 MASK

3.7 创建 MASK 函数
#

身份证号脱敏函数
#

CREATE OR REPLACE FUNCTION HR_TEST.MASK_ID_CARD
(
    P_ID_CARD VARCHAR2
)
RETURN VARCHAR2
AS
BEGIN
    RETURN SUBSTR(P_ID_CARD, 1, 6)
           || '********'
           || SUBSTR(P_ID_CARD, -4);
END;
/

SQL> CREATE OR REPLACE FUNCTION HR_TEST.MASK_ID_CARD
(
    P_ID_CARD VARCHAR2
)
RETURN VARCHAR2
AS
BEGIN
    RETURN SUBSTR(P_ID_CARD, 1, 6)
           || '********'
           || SUBSTR(P_ID_CARD, -4);
END;
/2   3   4   5   6   7   8   9   10  11  12
executed successfully
used time: 7.145(ms). Execute id is 1203.
SQL>

手机号脱敏函数
#

CREATE OR REPLACE FUNCTION HR_TEST.MASK_MOBILE
(
    P_MOBILE VARCHAR2
)
RETURN VARCHAR2
AS
BEGIN
    RETURN SUBSTR(P_MOBILE, 1, 3)
           || '****'
           || SUBSTR(P_MOBILE, -4);
END;
/

SQL> CREATE OR REPLACE FUNCTION HR_TEST.MASK_MOBILE
(
    P_MOBILE VARCHAR2
)
RETURN VARCHAR2
AS
BEGIN
    RETURN SUBSTR(P_MOBILE, 1, 3)
           || '****'
           || SUBSTR(P_MOBILE, -4);
END;
/2   3   4   5   6   7   8   9   10  11  12
executed successfully
used time: 7.131(ms). Execute id is 1204.
SQL>

工资脱敏函数
#

CREATE OR REPLACE FUNCTION HR_TEST.MASK_SALARY
(
    P_SALARY NUMBER
)
RETURN NUMBER
AS
BEGIN
    RETURN 0;
END;
/

SQL> CREATE OR REPLACE FUNCTION HR_TEST.MASK_SALARY
(
    P_SALARY NUMBER
)
RETURN NUMBER
AS
BEGIN
    RETURN 0;
END;
/2   3   4   5   6   7   8   9   10
executed successfully
used time: 7.257(ms). Execute id is 1205.
SQL>

3.8 创建 RLS Policy
#

BEGIN
    DBMS_RLS.ADD_POLICY(
        OBJECT_SCHEMA         => 'HR_TEST',
        OBJECT_NAME           => 'EMP_INFO',
        POLICY_NAME           => 'POL_EMP_INFO_MASK',
        FUNCTION_SCHEMA       => 'HR_TEST',
        POLICY_FUNCTION       => 'RLS_EMP_MASK_FUN',
        STATEMENT_TYPES       => 'SELECT',
        SEC_RELEVANT_COLS     => 'ID_CARD,MOBILE,SALARY',
        SEC_RELEVANT_COLS_OPT => DBMS_RLS.ALL_ROWS
    );
END;
/

SQL> BEGIN
    DBMS_RLS.ADD_POLICY(
        OBJECT_SCHEMA         => 'HR_TEST',
        OBJECT_NAME           => 'EMP_INFO',
        POLICY_NAME           => 'POL_EMP_INFO_MASK',
2   3   4   5   6           FUNCTION_SCHEMA       => 'HR_TEST',
        POLICY_FUNCTION       => 'RLS_EMP_MASK_FUN',
        STATEMENT_TYPES       => 'SELECT',
        SEC_RELEVANT_COLS     => 'ID_CARD,MOBILE,SALARY',
        SEC_RELEVANT_COLS_OPT => DBMS_RLS.ALL_ROWS
    );
7   8   9   10  11  12  END;
/13
DMSQL executed successfully
used time: 5.585(ms). Execute id is 1206.
SQL>

这里的关键参数说明:

参数说明
OBJECT_SCHEMA被保护对象所在模式
OBJECT_NAME被保护表或视图
POLICY_NAME策略名称
FUNCTION_SCHEMA策略函数所在模式
POLICY_FUNCTION返回策略谓词的函数
STATEMENT_TYPES生效 SQL 类型,本例为 SELECT
SEC_RELEVANT_COLS敏感列列表
SEC_RELEVANT_COLS_OPT指定 ALL_ROWS 后保留所有行,仅处理敏感列显示

3.9 绑定 MASK 函数
#

身份证号:

DBMS_RLS.ADD_MASK(
    'HR_TEST',
    'EMP_INFO',
    'ID_CARD',
    'HR_TEST',
    NULL,
    'MASK_ID_CARD'
);

SQL> DBMS_RLS.ADD_MASK(
    'HR_TEST',
    'EMP_INFO',
    'ID_CARD',
    'HR_TEST',
    NULL,
    'MASK_ID_CARD'
);2   3   4   5   6   7   8
DMSQL executed successfully
used time: 5.621(ms). Execute id is 1207.
SQL>

手机号:

DBMS_RLS.ADD_MASK(
    'HR_TEST',
    'EMP_INFO',
    'MOBILE',
    'HR_TEST',
    NULL,
    'MASK_MOBILE'
);

SQL> DBMS_RLS.ADD_MASK(
    'HR_TEST',
    'EMP_INFO',
    'MOBILE',
    'HR_TEST',
    NULL,
    'MASK_MOBILE'
);2   3   4   5   6   7   8
DMSQL executed successfully
used time: 3.423(ms). Execute id is 1208.
SQL>

工资:

DBMS_RLS.ADD_MASK(
    'HR_TEST',
    'EMP_INFO',
    'SALARY',
    'HR_TEST',
    NULL,
    'MASK_SALARY'
);

SQL> DBMS_RLS.ADD_MASK(
    'HR_TEST',
    'EMP_INFO',
    'SALARY',
    'HR_TEST',
    NULL,
    'MASK_SALARY'
);2   3   4   5   6   7   8
DMSQL executed successfully
used time: 3.313(ms). Execute id is 1209.
SQL>

3.10 达梦验证脱敏效果
#

普通用户或业务用户查询:

CONN HR_TEST/Dameng123
SELECT EMP_ID,EMP_NAME,ID_CARD,MOBILE,SALARY FROM HR_TEST.EMP_INFO;

验证结果:

SQL> CONN HR_TEST/Dameng123

Server[LOCALHOST:5236]:mode is normal, state is open
login used time : 3.265(ms)
SQL> SELECT EMP_ID,EMP_NAME,ID_CARD,MOBILE,SALARY FROM HR_TEST.EMP_INFO;

LINEID     EMP_ID      EMP_NAME ID_CARD            MOBILE      SALARY
---------- ----------- -------- ------------------ ----------- ------
1          1                    310101********1234 138****5678 0
2          2                    320102********6789 139****4321 0

used time: 4.411(ms). Execute id is 1502.
SQL>

3.11 达梦查看脱敏策略
#

查看 Policy
#

SQL> SELECT * FROM SYS.POLICIES;

LINEID     OBJID       POLICY_GROUP POLICY_NAME       PF_SCHNAME PKG_NAME FUN_NAME         SEL         DEL         INS         UPD         IND         CHK_OPTION  ENABLE      STATIC_POLICY POLICY_TYPE LONG_PREDICATE TRIGID
---------- ----------- ------------ ----------------- ---------- -------- ---------------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ------------- ----------- -------------- -----------
1          1035        SYS_DEFAULT  POL_EMP_INFO_MASK HR_TEST    NULL     RLS_EMP_MASK_FUN 3           0           0           0           0           0           1           0             5           0              -1

used time: 3.384(ms). Execute id is 1503.
SQL>

查看 MASK
#

SQL> SELECT * FROM SYS.DBA_POLICY_MASKS;

LINEID     OBJECT_OWNER OBJECT_NAME COLUMN_NAME FUNCTION_OWNER PACKAGE_NAME FUNCTION_NAME
---------- ------------ ----------- ----------- -------------- ------------ -------------
1          HR_TEST      EMP_INFO    ID_CARD     HR_TEST        NULL         MASK_ID_CARD
2          HR_TEST      EMP_INFO    MOBILE      HR_TEST        NULL         MASK_MOBILE
3          HR_TEST      EMP_INFO    SALARY      HR_TEST        NULL         MASK_SALARY

used time: 4.071(ms). Execute id is 1504.
SQL>

3.12 达梦管理员默认不脱敏
#

SYSDBA 默认绕过 RLS 脱敏策略。

[dmdba@dameng-srv ~]$ disql SYSDBA
password:

Server[LOCALHOST:5236]:mode is normal, state is open
login used time : 4.042(ms)
disql V8
SQL> SELECT EMP_ID,EMP_NAME,ID_CARD,MOBILE,SALARY FROM HR_TEST.EMP_INFO;

LINEID     EMP_ID      EMP_NAME ID_CARD            MOBILE      SALARY
---------- ----------- -------- ------------------ ----------- ------
1          1                    310101199001011234 13812345678 25000
2          2                    320102198805056789 13987654321 18000

used time: 3.265(ms). Execute id is 1201.
SQL>

实验验证结果:

用户是否脱敏
HR_TEST脱敏
SYSDBA不脱敏

四、Oracle 与达梦动态脱敏对比
#

对比项Oracle DBMS_REDACT达梦 DBMS_RLS
功能定位动态数据脱敏行级安全 + 动态脱敏
原始数据不修改不修改
生效阶段查询结果返回前查询策略改写与列掩码阶段
控制方式Redaction PolicyPolicy Function + Mask Function
多列处理首列 ADD_POLICY,后续列 ALTER_POLICY ADD_COLUMNSEC_RELEVANT_COLS 指定列,ADD_MASK 绑定函数
条件逻辑expression 为 TRUE 时脱敏本例中 RETURN '1=2' 触发掩码显示
管理员绕过SYSDBA / EXEMPT 权限用户不脱敏SYSDBA 不脱敏
复杂度较低,原生脱敏能力较高,但灵活性更强

五、总结
#

动态数据脱敏不是简单地把字段替换成星号,而是一种基于数据库策略的敏感数据访问控制能力。它的核心价值在于:真实数据仍然保存在数据库中,普通用户或应用查询时只看到脱敏结果,管理员或授权用户在受控场景下仍可访问原始数据,应用 SQL 通常无需大规模改造。

从本次实践看,Oracle 与达梦的实现路径不同,但目标一致:都希望在不改变原始数据的前提下,降低敏感信息在查询、运维、报表、测试和排障过程中的暴露风险。

无论 Oracle 还是达梦,动态脱敏都不是万能的数据安全方案。它解决的是“查询展示层面的敏感数据暴露”问题,不能替代权限最小化、账号治理、数据库审计、备份加密、网络访问控制和运维流程管控。尤其要注意,SYSDBA 等高权限管理员默认可以绕过脱敏策略,因此生产环境必须结合审计和权限管理共同落地。

参考资料
#

  1. Oracle Database PL/SQL Packages and Types Reference:DBMS_REDACT
  2. Oracle Advanced Security Guide:Configuring Oracle Data Redaction Policies
  3. Oracle Data Redaction Guide:Redacting Multiple Columns
  4. Oracle Error Help:ORA-28069 / ORA-28074
  5. 达梦官方文档:DBMS_RLS 包
  6. 达梦官方 FAQ:如何使图形化工具查询出来的数据不全是明文
  7. dbi-services 技术文章:Oracle Data Redaction or how to hide critical information