书接上文数据库高安全—审计追踪:传统审计&同一审计,从传统审计和同一审计两方面对高斯数据库的审计追踪技术进行解读,本篇将从数据动态脱敏方面对高斯数据库的数据保护技术进行解读。
5.1 数据动态脱敏
数据脱敏,顾名思义就是将敏感数据通过变形、屏蔽等方式处理惩罚,其目的是保护隐私数据信息,防止数据泄漏和恶意窥伺。当企业大概机构网络用户个人身份数据、手机、银行卡号等敏感信息,然后将数据通过导出(非生产情况)或直接查询(结合生产情况)的方式投入使用时,按照隐私保护相关法律法规需将数据进行“脱敏”处理惩罚。
openGauss实现了数据动态脱敏机制,它根据一系列用户配置的“脱敏计谋”来对查询命令进行分析匹配,最终将敏感数据屏蔽并返回。使用数据动态脱敏特性总的来说分为两个步调:配置脱敏计谋、触发脱敏计谋。本末节将对这两个步调进行详细分析。
显然,只有在配置脱敏计谋后系统才能有根据地进行敏感数据脱敏。openGauss提供了脱敏计谋配置(创建、修改、删除)语法,这些语法所涉及的语法剖析结点内容大致雷同,因此这里仅对创建计谋相关数据结构进行分析,其余不再赘述。下面将结合一个详细示例对数据动态脱敏特性进行详细先容。
表1给出了一张包罗敏感信息(薪资、银行卡号)的个人信息表,计谋管理员要对该表中的敏感信息创建脱敏计谋:当用户user1或user2在IP地点10.123.123.123上使用jdbc或gsql连接数据库并查询个人信息表时,系统将自动屏蔽敏感信息。
表1 个人信息表person
id
| name
| gender
| salary
| creditcards
| 1
| 张三
| 男
| 10000
| 6210630600006321083
| 2
| 李四
| 男
| 15000
| 6015431250003215514
| 3
| 王五
| 女
| 20000
| 5021134522201529881
| 起首计谋管理员必要对敏感列打标签,随后使用标签创建脱敏计谋,计谋配置DDL语句如下:
例1脱敏计谋配置示例。
配置资源标签:
- CREATE RESOURCE LABEL salary_label ADD COLUMN(person.salary);CREATE RESOURCE LABEL creditcard_label ADD COLUMN(person.creditcards);
复制代码 配置脱敏计谋:
- CREATE MASKING POLICY mask_person_policy MASKALL ON LABEL(salary_label), CREDITCARDMASKING ON label(creditcard_label) FILTER ON ROLES(user1,user2), IP(‘10.123.123.123’), APP(jdbc, gsql);
复制代码 user1在10.123.123.123地点使用gsql查询敏感数据:
- SELECT id, salary, creditcards from public.person;
复制代码 下面将对CREATE MASKING POLICY语句所涉及的语法结构定义进行逐一先容。
数据结构CreateMaskingPolicyStmt:
- typedef struct CreateMaskingPolicyStmt{
-
- NodeTag type;char *policy_name; // 脱敏策略名称List *policy_data; // 脱敏策略行为List *policy_filters; // 用户过滤条件bool policy_enabled; // 策略开关} CreateMaskingPolicyStmt;
复制代码 脱敏计谋创建语法是对CreateMaskingPolicyStmt进行添补,其中policy_data是由若干DefElem节点组成的List,每个DefElem指出了以何种方式脱敏数据库资源,DefElem->name标识脱敏方法,DefElem->arg代表脱敏对象。
数据动态脱敏中例1脱敏计谋配置示例的步调(3) 对应的policy_data构造结构如图1所示。
图1 脱敏计谋配置示例对应的policy_data构造结构
policy_filters描述了脱敏计谋生效的用户场景(用户名、客户端、登录IP),该List只有一个节点,是Policy Filter前缀逻辑树的根节点,只有当用户信息满足逻辑树所描述的场景时,其相应的脱敏计谋才会生效。Policy Filter逻辑树节点如下所示:
- typedef struct PolicyFilterNode{
-
- NodeTag type;char *node_type; // 逻辑操作类型,取值为“op”或“filter”char *op_value; // 逻辑操作符,仅当node_type为op时取值为“and”或“or”,否则为NULLchar *filter_type;// 过滤数据类型,仅当node_type为filter时取值为“APP”、“ROLES”、“IP”List *values; // 过滤数据值List,指出具体的过滤条件值,若node_type为op时置NULLNode *left; // 左子树Node *right; // 右子树} PolicyFilterNode;
复制代码 逻辑树节点分为操纵符(op)节点和过滤数据(filter)节点,当op节点分为“与”或“或”关系,其op_value将置为“and”或“or”,其左右子树代表操纵符左右子表达式。filter节点一般作为op的叶子节点出现,它标识详细的过滤信息并将其值存放在values链表中。必要注意的是,一个节点不可能既是op节点又是filter节点。数据动态脱敏中例1脱敏计谋配置示例的步调(3) 对应的policy_filters构造结构如图2所示。
图2 配置脱敏计谋对应的policy_filter构造结构
脱敏计谋配置的总体流程如图3所示。
图3 脱敏计谋配置流程图
在查询编译脱敏计谋配置SQL之后将进入计谋增删改主函数中,起首会根据语法剖析节点校验相关参数的正当性,做如下查抄:
- 查抄脱敏计谋指定的数据库资源是否存在。
- 查抄脱敏函数是否存在。
- 查抄脱敏计谋是否已存在。
- 查抄脱敏相关束缚:脱敏对象必须为根本表的数据列、脱敏列范例必须满足规格限定、脱敏列只允许加载一个脱敏函数。
- 查抄Masking Filter是否冲突,不允许同一数据库资源在雷同用户场景下触发多个计谋。
其中Masking Filter冲突校验的目的是为了防止用户场景同时满足多个脱敏计谋限定,导致计谋匹配时系统无法判定应该触发哪种脱敏计谋。因此在创建计谋时要包管其Filter与现存的计谋互斥,重要是判定是否存在一种用户场景可以或许同时满足多个MASKING FILTER。在数据动态脱敏所示的表2数据基础上,如下表中计谋A和计谋B是相互冲突的,而计谋A和计谋C是互斥的。
脱敏计谋冲突或互斥场景:
- 策略A:CREATE MASKING POLICY mask_A MASKALL ON LABEL(creditcard_label) FILTER ON IP(’10.123.123.123’), APP(jdbc), ROLES(user1);策略B:CREATE MASKING POLICY mask_B CREDITCARDMASKING ON LABEL(creditcard_label) FILTER ON IP(’10.123.123.123’,’10.90.132.132’), APP(jdbc, gsql), ROLES(user1);策略C:CREATE MASKING POLICY mask_C CREDITCARDMASKING ON LABEL(creditcard_label) FILTER ON IP(’10.123.123.123’ ,’10.90.132.132’), APP(jdbc), ROLES(user2);
复制代码 随后将依据计谋配置信息更新系统表:
- 更新gs_masking_policy系统表,存储policy根本信息。
- 更新gs_masking_policy_actions系统表,存储计谋对应的脱敏方式及脱敏对象。
- 更新gs_masking_policy_filter系统表,存储脱敏用户场景过滤信息,此时会将逻辑树转换为逻辑表达式字符串进行存储,在之后的敏感数据访问时该字符串将会重新转换为逻辑树进行场景校验。
为了降低计谋读取IO损耗,openGauss维护了一组线程级别的计谋缓存,用于保存已配置的脱敏计谋,并在计谋配置后进行实时刷新。
在用户进行数据查询时,数据动态脱敏特性使用openGauss的HOOK机制,将查询编译生成的查询树(Query)钩取出来与脱敏计谋进行匹配,最后将查询树按照脱敏计谋内容改写成不包罗敏感数据的“脱敏”查询树返还给剖析层继承实行,最终实现屏蔽敏感数据的能力。实在行流程图如4所示。
图4 脱敏计谋实行流程图
在对一个访问数据库资源的查询树进行脱敏之前,必要准备一份待匹配的脱敏计谋集合,其依据就是用户登录信息,check_masking_policy_filter函数的使命就是将用户信息与所有的脱敏计谋进行匹配,筛选出可能被查询触发的脱敏计谋。最终筛选如下脱敏计谋:
- 若脱敏计谋没有配置FILTER信息,阐明对所有效户生效。
- 若当前用户信息与脱敏计谋的FILTER匹配,则阐明对当前用户生效。
在每个脱敏计谋从系统表读入缓存时,必要将对应的FILTER逻辑表达式转换为逻辑树并将逻辑树根节点存入缓存中,将其作为脱敏计谋筛选条件,逻辑树结构如下。
- class PolicyLogicalTree {
-
- public: … bool parse_logical_expression(const gs_stl::gs_string logical_expr_str); // 逻辑表达式构造逻辑树入口函数 bool match(const FilterData *filter_item); bool has_intersect(PolicyLogicalTree *arg);private: gs_stl::gs_vector<PolicyLogicalNode> m_nodes; // 逻辑节点集合,包含了逻辑树中所有的节点 gs_stl::gs_vector<int> m_flat_tree; // 利用数组将逻辑节点索引构造逻辑二叉树 // 逻辑表达式转换为逻辑树的递归函数。bool parse_logical_expression_impl(const gs_stl::gs_string logical_expr_str, int *offset, int *idx, Edirection direction); inline void create_node(int *idx, EnodeType type, bool has_operator_not); // 创建单个逻辑树节点 void flatten_tree(); // 将逻辑树刷新到m_nodes集合与m_flat_tree索引中 bool check_apps_intersect(string_sort_vector*, string_sort_vector*); bool check_roles_intersect(oid_sort_vector*, oid_sort_vector*); bool m_has_ip; // 标识整个逻辑树是否涉及ip校验 bool m_has_role; // 标识整个逻辑树是否涉及用户名校验 bool m_has_app; // 标识整个逻辑树是否涉及客户端校验};
复制代码 逻辑树节点的结构与语法剖析中的FILTER节点类似,详细可以参照PolicyFilterNode结构。
- struct PolicyLogicalNode {
-
- ...EnodeType m_type;int m_left; // 左子节点索引int m_right; // 右子节点索引void make_eval(const FilterData *filter_item); //判断用户信息是否满足本节点子树表示的逻辑。bool m_eval_res;oid_sort_vector m_roles; // 本节点包含的用户名集合string_sort_vector m_apps; // 本节点包含的客户端名称集合IPRange m_ip_range; //本节点包含的IP};
复制代码 当必要将逻辑表达式转变为逻辑树时,parse_logical_expression_impl函数将对逻辑表达式字符串进行递归剖析,识别出表达式包罗的操纵符(and或or)以及Filter信息(ip、roles、app),构造出PolicyLogicalNode并使用左右子节点索引(m_left、m_right)链接起来形成逻辑树并将每个节点存入m_nodes中,最终使用m_nodes构造m_flat_tree数组来模拟二叉树。
m_flat_tree数组的作用是标记逻辑树节点间关系以及标识哪些节点是逻辑树的叶子节点,当用户信息与逻辑树某节点进行匹配时,起首必要与其左右子树进行匹配,然后根据该节点的逻辑运算符来判定是否满足Filter要求,而左右子树的判定效果又依靠于它们的子树的效果,因此这种递归判定方法起首将会是取叶子节点进行用户信息匹配。
openGauss使用“自底向上”的方式来进行用于信息与逻辑树的匹配,从m_flat_tree末端即叶子节点进行匹配,将匹配效果记录下来,当匹配到非叶子节点时(op节点)只需使用其左右子节点效果进行判定即可,最终实现整个逻辑树的匹配。在例1脱敏计谋配置示例中创建脱敏计谋后,当用户使用非受限的客户端访问敏感数据时,逻辑树匹配效果如图5所示。
图5 逻辑树匹配示例
在筛选出脱敏计谋后,就必要对查询树所有TargetEntry进行识别和计谋匹配,从openGauss源码可以看到,支持对SubLink、Aggref、OpExpr、RelabelType、FuncExpr、CoerceViaIO、Var范例的节点进行剖析识别。数据脱敏的核心思路是,Var范例节点代表了访问的数据库资源,而非Var范例节点可能包罗Var节点,因此必要根据其参数递归的探求Var节点,最后将识别到的所有Var节点进行计谋匹配并根据计谋内容进行节点替换。
识别脱敏结点源码:
- static bool mask_expr_node(ParseState *pstate, Expr*& expr, const policy_set *policy_ids, masking_result *result, List* rtable, bool can_mask){
-
- if (expr == NULL) {
-
- return false; } switch (nodeTag(expr)) {
-
- case T_SubLink: ... // 解析SubLink结点 case T_FuncExpr: ... // 解析FuncExpr结点 case T_Var: return handle_masking_node(pstate, expr, policy_ids, result, rtable, can_mask); // 进入最后脱敏处理过程 break; case T_RelabelType: ... // 解析RelabelType结点 case T_CoerceViaIO: ... // 解析CoerceViaIO结点 case T_Aggref: ... // 解析Aggref结点 case T_OpExpr: ... // 解析OpExpr结点 default: break; } return false;}
复制代码 在匹配脱敏计谋时,起首必要将识别出的Var节点进行剖析,将其转为PolicyLabelItem,该数据结构存储了数据列的全部路径信息,然后将其与已过滤出的脱敏计谋集合进行匹配,若某个脱敏计谋对应的数据库资源对象与PolicyLabelItem同等,将已匹配到的脱敏计谋指定的方式替换该Var节点,相关数据结构如下:数据结构PolicyLabelItem。
- struct PolicyLabelItem {
-
- ... void get_fqdn_value(gs_stl::gs_string *value) const;
- bool operator < (const PolicyLabelItem& arg) const; bool operator == (const PolicyLabelItem& arg) const; bool empty() const {return strlen(m_column) == 0;} void set_object(const char *obj, int obj_type = 0); void set_object(Oid objid, int obj_type = 0); Oid m_schema; // 数据库资源所属的namespace Oid Oid m_object; // 数据库资源所属的table Oid char m_column[256];// 列名 int m_obj_type; // 资源类型,数据动态脱敏仅支持对COLUMN生效};
复制代码 脱敏计谋匹配乐成后,将会根据计谋内容替换包罗敏感信息的Var节点,使之外嵌脱敏函数。最后将修改后的查询树返还给剖析器继承实行,最终敏感数据将会在脱敏函数的作用下返回,以脱敏的情势返回给客户端。数据动态脱敏中例1脱敏计谋配置示例里使用gsql查询敏感数据的步调(4) 中SELECT语句对应的查询树脱敏前的数据构造结构如图6所示。
图6 脱敏结点替换示例
至此,整个查询树已经完成了脱敏计谋的匹配与重写,随后将重新回归查询剖析模块并继承实行后续处理惩罚,最终系统将返回脱敏后的数据效果。
以上内容从数据动态脱敏方面对高斯数据库的数据保护技术进行相识读,下篇讲从数据透明加密方面继承先容高斯数据库的数据保护。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |