网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
char filter_type;/ 过滤数据类型,仅当node_type为filter时取值为“APP”、“ROLES”、“IP” */
List values; / 过滤数据值List,指出具体的过滤条件值,若node_type为op时置NULL */
Node left; / 左子树 */
Node right; / 右子树 */
} PolicyFilterNode;
逻辑树节点分为操作符(op)节点和过滤数据(filter)节点。当op节点分为“与”或“或”关系,其op\_value将置为“and”或“or”,其左右子树代表操作符左右子表达式。filter节点一般作为op的叶子节点出现,它标识具体的过滤信息并将其值存放在values链表中。需要注意的是,一个节点不可能既是op节点又是filter节点。“9.6.2 数据动态脱敏”小节中例9-1脱敏策略配置示例的步骤0对应的policy\_filters组织结构如图9-32所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/0ee9bb5fb44b4c2bace5c7597fb3c805.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAR2F1c3Pmnb7pvKDkvJo=,size_15,color_FFFFFF,t_70,g_se,x_16)
图9-32 配置脱敏策略对应的policy\_filters 组织结构
脱敏策略配置的总体流程如图9-33所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/a6c84650ed37493e9e5e4862dd6f30e3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAR2F1c3Pmnb7pvKDkvJo=,size_10,color_FFFFFF,t_70,g_se,x_16)
图9-33 脱敏策略配置流程图
在查询编译脱敏策略配置SQL之后将进入策略增删改主函数中,首先会根据语法解析节点校验相关参数的合法性,做如下检查:
(1) 检查脱敏策略指定的数据库资源是否存在。
(2) 检查脱敏函数是否存在。
(3) 检查脱敏策略是否已存在。
(4) 检查脱敏相关约束:脱敏对象必须为基本表的数据列、脱敏列类型必须满足规格限制、脱敏列只允许加载一个脱敏函数。
(5) 检查Masking Filter是否冲突,不允许同一数据库资源在相同用户场景下触发多个策略。
其中Masking Filter冲突校验的目的是防止用户场景同时满足多个脱敏策略限制,导致策略匹配时系统无法判断应该触发哪种脱敏策略。因此在创建策略时要保证其过滤条件与现存的策略互斥,主要是判断是否存在一种用户场景能够同时满足多个MASKING FILTER。在“9.6.2 数据动态脱敏”小节所示的表9-6数据基础上,如下表中策略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);
随后将依据策略配置信息更新系统表:
(1) 更新gs\_masking\_policy系统表,存储policy基本信息。
(2) 更新gs\_masking\_policy\_actions系统表,存储策略对应的脱敏方式及脱敏对象。
(3) 更新gs\_masking\_policy\_filter系统表,存储脱敏用户场景过滤信息。此时会将逻辑树转换为逻辑表达式字符串进行存储,在之后的敏感数据访问时该字符串将会重新转换为逻辑树进行场景校验。
为了降低策略读取I/O损耗,openGauss维护了一组线程级别的策略缓存,用于保存已配置的脱敏策略,并在策略配置后进行实时刷新。
在用户进行数据查询时,数据动态脱敏特性使用openGauss的HOOK机制,将查询编译生成的查询树钩取出来与脱敏策略进行匹配,最后将查询树按照脱敏策略内容改写成不包含敏感数据的“脱敏”查询树返还给解析层继续执行,最终实现屏蔽敏感数据的能力。其执行流程如图9-34所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/9d9ffd04b6d944c09471e7b2999ff876.png)
图9-34 脱敏策略执行流程图
在对一个访问数据库资源的查询树进行脱敏之前,需要准备一份待匹配的脱敏策略集合,其依据就是用户登录信息,check\_masking\_policy\_filter函数的任务就是将用户信息与所有的脱敏策略进行匹配,筛选出可能被查询触发的脱敏策略。最终筛选如下脱敏策略。
(1) 若脱敏策略没有配置过滤条件信息,说明对所有用户生效。
(2) 若当前用户信息与脱敏策略的过滤条件匹配,则说明对当前用户生效。
在每个脱敏策略从系统表读入缓存时,需要将对应的过滤条件逻辑表达式转换为逻辑树并将逻辑树根节点存入缓存中,将其作为脱敏策略筛选条件。逻辑树结构代码如下:
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 m_nodes; / 逻辑节点集合,包含了逻辑树中所有的节点 /
gs_stl::gs_vector 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)以及过滤条件信息(ip、roles、app),构造出PolicyLogicalNode并使用左右子节点索引(m\_left、m\_right)链接起来形成逻辑树并将每个节点存入m\_nodes中,最终利用m\_nodes构造m\_flat\_tree数组来模拟二叉树。
m\_flat\_tree数组的作用是标记逻辑树节点间关系以及标识哪些节点是逻辑树的叶子节点。当用户信息与逻辑树某节点进行匹配时,首先需要与其左右子树进行匹配,然后根据该节点的逻辑运算符来判断是否满足过滤条件要求,而左右子树的判断结果又依赖于它们的子树的结果,因此这种递归判断方法首先将会是取叶子节点进行用户信息匹配。
openGauss使用“自底向上”的方式来进行用于信息与逻辑树的匹配。从m\_flat\_tree末尾(叶子节点)进行匹配,将匹配结果记录下来,当匹配到非叶子节点时(op节点)只需使用其左右子节点结果进行判断即可,最终实现整个逻辑树的匹配。在例9-1脱敏策略配置示例中创建脱敏策略后,当用户使用非受限的客户端访问敏感数据时,逻辑树匹配结果如图9-35所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/79db5dc9adcb49cf8dc7ef359a64d81c.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAR2F1c3Pmnb7pvKDkvJo=,size_15,color_FFFFFF,t_70,g_se,x_16)
图9-35 逻辑树匹配示例
在筛选出脱敏策略后,就需要对查询树所有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的代码如下:
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新