zoukankan      html  css  js  c++  java
  • 数据脱敏:数仓安全隐私保护见真招儿

    摘要:如何加强技术层面的数据安全和隐私保护,对数据仓库产品本身提出更多的功能要求,也是数据安全建设最行之有效的办法。

    引言

    大数据时代的到来,颠覆了传统业态的运作模式,激发出新的生产潜能。数据成为重要的生产要素,是信息的载体,数据间的流动也潜藏着更高阶维度的价值信息。对于数据控制者和数据处理者而言,如何最大化数据流动的价值,是数据挖掘的初衷和意义。然而,一系列信息泄露事件的曝光,使得数据安全越来越受到广泛的关注。

    各国各地区逐步建立健全和完善数据安全与隐私保护相关法律法规,提供用户隐私保护的法律保障。如何加强技术层面的数据安全和隐私保护,对数据仓库产品本身提出更多的功能要求,也是数据安全建设最行之有效的办法。

    什么是数据脱敏?

    数据脱敏(Data Masking),顾名思义,是屏蔽敏感数据,对某些敏感信息(比如,身份证号、手机号、卡号、客户姓名、客户地址、邮箱地址、薪资等等 )通过脱敏规则进行数据的变形,实现隐私数据的可靠保护。业界常见的脱敏规则有,替换、重排、加密、截断、掩码,用户也可以根据期望的脱敏算法自定义脱敏规则。

    通常,良好的数据脱敏实施,需要遵循如下两个原则,第一,尽可能地为脱敏后的应用,保留脱敏前的有意义信息;第二,最大程度地防止黑客进行破解。

    数据脱敏分为静态数据脱敏和动态数据脱敏。静态数据脱敏,是数据的“搬移并仿真替换”,是将数据抽取进行脱敏处理后,下发给下游环节,随意取用和读写的,脱敏后数据与生产环境相隔离,满足业务需求的同时保障生产数据库的安全。动态数据脱敏,在访问敏感数据的同时实时进行脱敏处理,可以为不同角色、不同权限、不同数据类型执行不同的脱敏方案,从而确保返回的数据可用而安全。

    GaussDB (DWS)的数据脱敏功能,摒弃业务应用层脱敏依赖性高、代价大等痛点,将数据脱敏内化为数据库产品自身的安全能力,提供了一套完整、安全、灵活、透明、友好的数据脱敏解决方案,属于动态数据脱敏。用户识别敏感字段后,基于目标字段,绑定内置脱敏函数,即可创建脱敏策略。脱敏策略(Redaction Policy)与表对象是一一对应的。一个脱敏策略包含表对象、生效条件、脱敏列-脱敏函数对三个关键要素,是该表对象上所有脱敏列的集合,不同字段可以根据数据特征采用不同的脱敏函数。当且仅当生效条件为真时,查询语句才会触发敏感数据的脱敏,而脱敏过程是内置在SQL引擎内部实现的,对生成环境用户是透明不可见的。

    怎么用数据脱敏?

    动态数据脱敏,是在查询语句执行过程中,根据生效条件是否满足,实现实时的脱敏处理。生效条件,通常是针对当前用户角色的判断。敏感数据的可见范围,即是针对不同用户预设的。系统管理员,具有最高权限,任何时刻对任何表的任何字段都可见。确定受限制用户角色,是创建脱敏策略的第一步。

    敏感信息依赖于实际业务场景和安全维度,以自然人为例,用户个体的敏感字段包括:姓名、身份证号、手机号、邮箱地址等等;在银行系统,作为客户,可能还涉及银行卡号、过期时间、支付密码等等;在公司系统,作为员工,可能还涉及薪资、教育背景等;在医疗系统,作为患者,可能还涉及就诊信息等等。所以,识别和梳理具体业务场景的敏感字段,是创建脱敏策略的第二步。

    产品内置一系列常见的脱敏函数接口,可以针对不同数据类型和数据特征,指定参数,从而达到不一样的脱敏效果。脱敏函数可采用如下三种内置接口,同时支持自定义脱敏函数。三种内置脱敏函数能够涵盖大部分场景的脱敏效果,不推荐使用自定义脱敏函数。

    • MASK_NONE:不作脱敏处理,仅内部测试用。
    • MASK_FULL:全脱敏成固定值。
    • MASK_PARTIAL:使用指定的脱敏字符对脱敏范围内的内容做部分脱敏。

    不同脱敏列可以采用不同的脱敏函数。比如,手机号通常显示后四位尾号,前面用"*"替换;金额统一显示为固定值0,等等。确定脱敏列需要绑定的脱敏函数,是创建脱敏策略的第三步。

    以某公司员工表emp,表的属主用户alice以及用户matu、july为例,简单介绍数据脱敏的使用过程。其中,表emp包含员工的姓名、手机号、邮箱、发薪卡号、薪资等隐私数据,用户alice是人力资源经理,用户matu和july是普通职员。

    假设表、用户及用户对表emp的查看权限均已就绪。

    • (1)创建脱敏策略mask_emp,仅允许alice查看员工所有信息,matu和july对发薪卡号、薪资均不可见。字段card_no是数值类型,采用MASK_FULL全脱敏成固定值0;字段card_string是字符类型,采用MASK_PARTIAL按指定的输入输出格式对原始数据作部分脱敏;字段salary是数值类型,采用数字9部分脱敏倒数第二位前的所有数位值。
    postgres=# CREATE REDACTION POLICY mask_emp ON emp WHEN (current_user != 'alice')
    ADD COLUMN card_no WITH mask_full(card_no),
    ADD COLUMN card_string WITH mask_partial(card_string, 'VVVVFVVVVFVVVVFVVVV','VVVV-VVVV-VVVV-VVVV','#',1,12), 
    ADD COLUMN salary WITH mask_partial(salary, '9', 1, length(salary) - 2);

    切换到matu和july,查看员工表emp。

    postgres=> SET ROLE matu PASSWORD 'Gauss@123';
    postgres=> SELECT * FROM emp;
     id | name |  phone_no   | card_no |     card_string     |        email         |   salary   |      birthday       
    ----+------+-------------+---------+---------------------+----------------------+------------+---------------------
      1 | anny | 13420002340 |       0 | ####-####-####-1234 | smithWu@163.com      | 99999.9990 | 1999-10-02 00:00:00
      2 | bob  | 18299023211 |       0 | ####-####-####-3456 | 66allen_mm@qq.com    |  9999.9990 | 1989-12-12 00:00:00
      3 | cici | 15512231233 |         |                     | jonesishere@sina.com |            | 1992-11-06 00:00:00
    (3 rows)
    postgres=> SET ROLE july PASSWORD 'Gauss@123';
    postgres=> SELECT * FROM emp;
     id | name |  phone_no   | card_no |     card_string     |        email         |   salary   |      birthday       
    ----+------+-------------+---------+---------------------+----------------------+------------+---------------------
      1 | anny | 13420002340 |       0 | ####-####-####-1234 | smithWu@163.com      | 99999.9990 | 1999-10-02 00:00:00
      2 | bob  | 18299023211 |       0 | ####-####-####-3456 | 66allen_mm@qq.com    |  9999.9990 | 1989-12-12 00:00:00
      3 | cici | 15512231233 |         |                     | jonesishere@sina.com |            | 1992-11-06 00:00:00
    (3 rows)
    • (2)由于工作调整,matu进入人力资源部参与公司招聘事宜,也对员工所有信息可见,修改策略生效条件。
    postgres=> ALTER REDACTION POLICY mask_emp ON emp WHEN(current_user NOT IN ('alice', 'matu'));
    切换到用户matu和july,重新查看员工表emp。
    postgres=> SET ROLE matu PASSWORD 'Gauss@123';
    postgres=> SELECT * FROM emp;
     id | name |  phone_no   |     card_no      |     card_string     |        email         |   salary   |      birthday       
    ----+------+-------------+------------------+---------------------+----------------------+------------+---------------------
      1 | anny | 13420002340 | 1234123412341234 | 1234-1234-1234-1234 | smithWu@163.com      | 10000.0000 | 1999-10-02 00:00:00
      2 | bob  | 18299023211 | 3456345634563456 | 3456-3456-3456-3456 | 66allen_mm@qq.com    |  9999.9900 | 1989-12-12 00:00:00
      3 | cici | 15512231233 |                  |                     | jonesishere@sina.com |            | 1992-11-06 00:00:00
    (3 rows)
    postgres=> SET ROLE july PASSWORD 'Gauss@123';
    postgres=> SELECT * FROM emp;
     id | name |  phone_no   | card_no |     card_string     |        email         |   salary   |      birthday       
    ----+------+-------------+---------+---------------------+----------------------+------------+---------------------
      1 | anny | 13420002340 |       0 | ####-####-####-1234 | smithWu@163.com      | 99999.9990 | 1999-10-02 00:00:00
      2 | bob  | 18299023211 |       0 | ####-####-####-3456 | 66allen_mm@qq.com    |  9999.9990 | 1989-12-12 00:00:00
      3 | cici | 15512231233 |         |                     | jonesishere@sina.com |            | 1992-11-06 00:00:00
    (3 rows)
    • (3)员工信息phone_no、email和birthday也是隐私数据,更新脱敏策略mask_emp,新增三个脱敏列。
    postgres=> ALTER REDACTION POLICY mask_emp ON emp ADD COLUMN phone_no WITH mask_partial(phone_no, '*', 4);
    postgres=> ALTER REDACTION POLICY mask_emp ON emp ADD COLUMN email WITH mask_partial(email, '*', 1, position('@' in email));
    postgres=> ALTER REDACTION POLICY mask_emp ON emp ADD COLUMN birthday WITH mask_full(birthday);

    切换到用户july,查看员工表emp。

    postgres=> SET ROLE july PASSWORD 'Gauss@123';
    postgres=> SELECT * FROM emp;
     id | name |  phone_no   | card_no |     card_string     |        email         |   salary   |      birthday       
    ----+------+-------------+---------+---------------------+----------------------+------------+---------------------
      1 | anny | 134******** |       0 | ####-####-####-1234 | ********163.com      | 99999.9990 | 1970-01-01 00:00:00
      2 | bob  | 182******** |       0 | ####-####-####-3456 | ***********qq.com    |  9999.9990 | 1970-01-01 00:00:00
      3 | cici | 155******** |         |                     | ************sina.com |            | 1970-01-01 00:00:00
    (3 rows)
    • (4)考虑用户交互的友好性,GaussDB (DWS) 提供系统视图redaction_policies和redaction_columns,方便用户直接查看更多脱敏信息。
    postgres=> SELECT * FROM redaction_policies;
     object_schema | object_owner | object_name | policy_name |            expression             | enable | policy_description 
    ---------------+--------------+-------------+-------------+-----------------------------------+--------+--------------------
     public        | alice        | emp         | mask_emp    | ("current_user"() = 'july'::name) | t      | 
    (1 row)
    postgres=> SELECT object_name, column_name, function_info FROM redaction_columns;
     object_name | column_name |                                             function_info                                             
    -------------+-------------+-------------------------------------------------------------------------------------------------------
     emp         | card_no     | mask_full(card_no)
     emp         | card_string | mask_partial(card_string, 'VVVVFVVVVFVVVVFVVVV'::text, 'VVVV-VVVV-VVVV-VVVV'::text, '#'::text, 1, 12)
     emp         | email       | mask_partial(email, '*'::text, 1, "position"(email, '@'::text))
     emp         | salary      | mask_partial(salary, '9'::text, 1, (length((salary)::text) - 2))
     emp         | birthday    | mask_full(birthday)
     emp         | phone_no    | mask_partial(phone_no, '*'::text, 4)
    (6 rows)
    • (5)突然某一天,公司内部可共享员工信息时,直接删除表emp的脱敏策略mask_emp即可。
    postgres=> DROP REDACTION POLICY mask_emp ON emp;

    更多用法详情,请参考GaussDB (DWS) 8.1.1产品文档。

    数据脱敏实现背后的秘密

    GaussDB (DWS)数据脱敏功能,基于SQL引擎既有的实现框架,在受限用户执行查询语句过程中,实现外部不感知的实时脱敏处理。关于其内部实现,如上图所示。我们将脱敏策略(Redaction Policy)视为表对象上绑定的规则,在优化器查询重写阶段,遍历Query Tree中TargetList的每个TargetEntry,如若涉及基表的某个脱敏列,且当前脱敏规则生效(即满足脱敏策略的生效条件且enable开启状态),则断定此TargetEntry中涉及要脱敏的Var对象,此时,遍历脱敏列系统表pg_redaction_column,查找到对应脱敏列绑定的脱敏函数,将其替换成对应的FuncExpr即可。经过上述对Query Tree的重写处理,优化器会自动生成新的执行计划,执行器遵照新的计划执行,查询结果将对敏感数据做脱敏处理。

    带有数据脱敏的语句执行,相较于原始语句,增加了数据脱敏的逻辑处理,势必会给查询带来额外的开销。这部分开销,主要受表的数据规模、查询目标列涉及的脱敏列数、脱敏列采用的脱敏函数三方面因素影响。

    针对简单查询语句,以tpch表customer为例,针对上述因素展开测试,如下图所示。

    图(a)、(b)中基表customer根据字段类型和特征,既有采用MASK_FULL脱敏函数的,也有采用MASK_PARTIAL脱敏函数的。MASK_FULL对于任何长度和类型的原始数据,均只脱敏成固定值,所以,输出结果相较于原始数据,差异很大。图(a)显示不同数据规模下,脱敏和非脱敏场景简单查询语句的执行耗时。实心图标为非脱敏场景,空心图标为被限制用户,即脱敏场景。

    可见,数据规模越大,带有脱敏的查询耗时与原始语句差异越大。图(b)显示10x数据规模下查询涉及脱敏列数不同对于语句执行性能的影响。涉及1列脱敏列时,带有脱敏的查询比原始语句慢,追溯发现,此列采用的是MASK_PARTIAL部分脱敏函数,查询结果只是改变了结果的格式,结果内容的长度并未变化,符合“带有脱敏的语句执行会有相应的性能劣化”的理论猜想。随着查询涉及脱敏列数的增加,我们发现一个奇怪的现象,脱敏场景反倒比原始语句执行更快。进一步追溯多列场景下脱敏列关联的脱敏函数,发现,正是因为存在使用MASK_FULL全脱敏函数的脱敏列,导致输出结果集部分相比原始数据节省很多时间开销,从而多列查询下带有数据脱敏的简单查询反倒提速不少。

    为了佐证上述猜测,我们调整脱敏函数,所有脱敏列均采用MASK_PARTIAL对原始数据做部分脱敏,从而能够在脱敏结果上保留原始数据的外部可读性。于是,如图(c)所示,当脱敏列均关联部分脱敏函数时,带有数据脱敏的语句比原始语句劣化10%左右,理论上讲,这种劣化是在可接受范围的。上述测试仅针对简单的查询语句,当语句复杂到带有聚集函数或复杂表达式运算时,可能这种性能劣化会更明显。

    总结

    GaussDB (DWS)产品数据脱敏功能,是数据库产品内化和夯实数据安全能力的重要技术突破,主要涵盖以下三个方面:

    1. 一套简单、易用的数据脱敏策略语法;
    2. 一系列可覆盖常见隐私数据脱敏效果的、灵活配置的内置脱敏函数;
    3. 一个完备、便捷的脱敏策略应用方案,使得原始语句在执行过程中可以实时、透明、高效地实现脱敏。

    总而言之,此数据脱敏功能可以充分满足客户业务场景的数据脱敏诉求,支持常见隐私数据的脱敏效果,实现敏感数据的可靠保护。

     本文分享自华为云社区《GaussDB(DWS)安全:隐私保护现真招儿——数据脱敏》,原文作者:wo华哒哒。

    点击关注,第一时间了解华为云新鲜技术~

  • 相关阅读:
    数据类型装换
    变量及数据类型
    27 网络通信协议 udp tcp
    26 socket简单操作
    26 socket简单操作
    14 内置函数 递归 二分法查找
    15 装饰器 开闭原则 代参装饰器 多个装饰器同一函数应用
    12 生成器和生成器函数以及各种推导式
    13 内置函数 匿名函数 eval,exec,compile
    10 函数进阶 动态传参 作用域和名称空间 函数的嵌套 全局变量
  • 原文地址:https://www.cnblogs.com/huaweiyun/p/14669773.html
Copyright © 2011-2022 走看看