使用动态数据掩码

本主题说明如何在 Snowflake 中配置和使用动态数据掩码。

要了解有关使用带有标签的掩码策略的更多信息,请参阅 基于标签的掩码策略

使用动态数据掩码

下面列出了在 Snowflake 中配置和使用动态数据掩码的简要步骤:

  1. 向安全或隐私官的自定义角色授予掩码策略管理权限。

  2. 将该自定义角色授予相应的用户。

  3. 安全或隐私官创建和定义掩码策略,并将其应用于包含敏感数据的列。

  4. 在 Snowflake 中执行查询。请注意以下事项:

    • Snowflake 会动态重写查询,并将掩码策略 SQL 表达式应用于列。

    • 对于在掩码策略中指定的列,查询中出现该列的每个位置(例如投影、join 谓词、where 子句谓词、order by 和 group by)都会发生列重写。

    • 用户根据掩码策略中定义的执行上下文条件查看掩码数据。有关动态数据掩码策略中的执行上下文的更多信息,请参阅 高级列级安全主题

第 1 步:向自定义角色授予掩码策略权限

安全或隐私官 应担任掩码策略管理员(即自定义角色:MASKING_ADMIN),并应具有掩码策略的定义、管理和应用到列的权限。

Snowflake 为列级安全掩码策略提供以下可授予安全或隐私官的权限:

权限

描述

CREATE MASKING POLICY

此架构级权限控制谁可以创建掩码策略。

APPLY MASKING POLICY

此账户级权限控制谁可以在列上设置和取消设置掩码策略,并且默认授予 ACCOUNTADMIN 角色。. 此权限仅允许将掩码策略应用于列,并且 提供 访问控制权限 中描述的任何其他表权限。

APPLY ON MASKING POLICY

可选。策略所有者可以使用此策略级权限,将在列上设置和取消设置给定掩码策略的操作分散给对象所有者(即具有对象的 OWNERSHIP 权限的角色)。. Snowflake 支持 自主访问控制,其中对象所有者也视为数据管理员。. 如果策略管理员信赖对象所有者是受保护列的数据管理员,则策略管理员可以使用此权限来分散策略设置和取消设置操作的应用。

以下示例创建 MASKING_ADMIN 角色,并向该角色授予掩码策略权限。

创建掩码策略管理员自定义角色:

use role useradmin;
CREATE ROLE masking_admin;
Copy

masking_admin 角色授予权限:

use role securityadmin;
GRANT CREATE MASKING POLICY on SCHEMA <db_name.schema_name> to ROLE masking_admin;
GRANT APPLY MASKING POLICY on ACCOUNT to ROLE masking_admin;
Copy

允许 table_owner 角色设置或取消设置 ssn_mask 掩码策略(可选):

GRANT APPLY ON MASKING POLICY ssn_mask to ROLE table_owner;
Copy

其中:

  • db_name.schema_name

    指定应为其授予权限的架构的标识符。

有关更多信息,请参阅:

第 2 步:向用户授予自定义角色

MASKING_ADMIN 自定义角色授予充当安全或隐私官的用户。

GRANT ROLE masking_admin TO USER jsmith;
Copy

第 3 步:创建掩码策略

使用 MASKING_ADMIN 角色创建掩码策略并将其应用于列。

在此代表性示例中,具有 ANALYST 角色的用户将看到未掩码的值。没有 ANALYST 角色的用户将看到完整的掩码。

CREATE OR REPLACE MASKING POLICY email_mask AS (val string) RETURNS string ->
  CASE
    WHEN CURRENT_ROLE() IN ('ANALYST') THEN val
    ELSE '*********'
  END;
Copy

小技巧

如果您想要更新现有的掩码策略,并需要查看该策略的当前定义,请调用 GET_DDL 函数或运行 DESCRIBE MASKING POLICY 命令。

第 4 步:将掩码策略应用于表或视图列

这些示例假定在创建表时未将掩码策略应用于表列,在创建视图时也未应用于视图列。在使用 CREATE TABLE 语句创建表,或者在使用 CREATE VIEW 语句创建视图时,可以根据需要将掩码策略应用于表列或视图列。

执行以下语句,以将策略应用于表列或视图列。

-- apply masking policy to a table column

ALTER TABLE IF EXISTS user_info MODIFY COLUMN email SET MASKING POLICY email_mask;

-- apply the masking policy to a view column

ALTER VIEW user_info_v MODIFY COLUMN email SET MASKING POLICY email_mask;
Copy

第 5 步:查询 Snowflake 中的数据

在 Snowflake 中执行两个不同的查询(一个使用 ANALYST 角色,另一个使用其他角色),以验证没有 ANALYST 角色的用户是否看到完整的掩码。

-- using the ANALYST role

USE ROLE analyst;
SELECT email FROM user_info; -- should see plain text value

-- using the PUBLIC role

USE ROLE PUBLIC;
SELECT email FROM user_info; -- should see full data mask
Copy

具有可记忆函数的掩码策略

此示例使用 可记忆函数 来缓存映射表上的查询结果,该结果用于确定某个角色是否有权查看 PII 数据。数据工程师使用掩码策略来保护表中的列。

下面的过程将引用这些对象:

  • 包含 PII 数据的表 employee_data

    +----------+-------------+---------------+
    | USERNAME |     ID      | PHONE_NUMBER  |
    +----------+-------------+---------------+
    | JSMITH   | 12-3456-89  | 1555-523-8790 |
    | AJONES   | 12-0124-32  | 1555-125-1548 |
    +----------+-------------+---------------+
    
  • 确定特定角色是否有权查看数据的映射表 auth_role_t

    +---------------+---------------+
    | ROLE          | IS_AUTHORIZED |
    +---------------+---------------+
    | DATA_ENGINEER | TRUE          |
    | DATA_STEWARD  | TRUE          |
    | IT_ADMIN      | TRUE          |
    | PUBLIC        | FALSE         |
    +---------------+---------------+
    

完成以下步骤,创建一个可调用带实参的可记忆函数的掩码策略:

  1. 创建一个可记忆函数,用于查询映射表。该函数根据 is_authorized 列的值返回角色数组:

    CREATE FUNCTION is_role_authorized(arg1 VARCHAR)
    RETURNS BOOLEAN
    MEMOIZABLE
    AS
    $$
      SELECT ARRAY_CONTAINS(
        arg1::VARIANT,
        (SELECT ARRAY_AGG(role) FROM auth_role WHERE is_authorized = TRUE)
      )
    $$;
    
    Copy
  2. 调用可记忆函数,缓存查询结果。在本例中,传递值 TRUE 作为实参值,因为结果数组充当允许访问受掩码策略保护的数据的角色来源:

    SELECT is_role_authorized(IT_ADMIN);
    
    Copy
    +---------------------------------------------+
    |         is_role_authorized(IT_ADMIN)        |
    +---------------------------------------------+
    |                    TRUE                     |
    +---------------------------------------------+
    
  3. 创建一个掩码策略来保护 id 列。该策略会调用可记忆函数,来确定用于查询表的角色是否获得授权来查看受保护列中的数据:

    CREATE OR REPLACE MASKING POLICY empl_id_mem_mask
    AS (val VARCHAR) RETURNS VARCHAR ->
    CASE
      WHEN is_role_authorized(CURRENT_ROLE()) THEN val
      ELSE NULL
    END;
    
    Copy
  4. 使用 ALTER TABLE ...ALTER COLUMN 命令设置表的掩码策略:

    ALTER TABLE employee_data MODIFY COLUMN id
      SET MASKING POLICY empl_id_mem_mask;
    
    Copy
  5. 查询表以测试策略:

    USE ROLE data_engineer;
    SELECT * FROM employee_data;
    
    Copy

    该查询返回未掩码数据。

    但是,如果将角色切换为 PUBLIC 角色并重复本步骤中的查询,则 id 中的值将替换为 NULL

其他掩码策略示例

以下是可在动态数据掩码策略正文中使用的其他代表性示例。

允许生产 账户 查看未掩码的值,并允许所有其他账户(例如开发、测试)查看掩码值。

case
  when current_account() in ('<prod_account_identifier>') then val
  else '*********'
end;
Copy

为未经授权的用户返回 NULL:

case
  when current_role() IN ('ANALYST') then val
  else NULL
end;
Copy

为未经授权的用户返回静态的掩码值:

CASE
  WHEN current_role() IN ('ANALYST') THEN val
  ELSE '********'
END;
Copy

使用 SHA2、SHA2_HEX 为未经授权的用户返回哈希值。在掩码策略中使用哈希函数可能会导致冲突,因此,请谨慎使用此方法。有关更多信息,请参阅 高级列级安全主题

CASE
  WHEN current_role() IN ('ANALYST') THEN val
  ELSE sha2(val) -- return hash of the column value
END;
Copy

应用部分掩码或完整掩码:

CASE
  WHEN current_role() IN ('ANALYST') THEN val
  WHEN current_role() IN ('SUPPORT') THEN regexp_replace(val,'.+\@','*****@') -- leave email domain unmasked
  ELSE '********'
END;
Copy

使用时间戳。

case
  WHEN current_role() in ('SUPPORT') THEN val
  else date_from_parts(0001, 01, 01)::timestamp_ntz -- returns 0001-01-01 00:00:00.000
end;
Copy

重要

目前,Snowflake 不支持掩码策略中的不同输入和输出数据类型,例如定义掩码策略时以时间戳为目标,但返回字符串(例如 ***MASKED***);输入和输出数据类型必须匹配。

解决方法是使用虚构的时间戳值转换实际时间戳值。有关更多信息,请参阅 DATE_FROM_PARTSCAST、::

使用 UDF:

CASE
  WHEN current_role() IN ('ANALYST') THEN val
  ELSE mask_udf(val) -- custom masking function
END;
Copy

作用于变体数据:

CASE
   WHEN current_role() IN ('ANALYST') THEN val
   ELSE OBJECT_INSERT(val, 'USER_IPADDRESS', '****', true)
END;
Copy

使用自定义授权表。请注意在 WHEN 子句中使用了 EXISTS。在掩码策略正文中包含子查询时,请务必使用 EXISTS。有关 Snowflake 支持的子查询的更多信息,请参阅 使用子查询

CASE
  WHEN EXISTS
    (SELECT role FROM <db>.<schema>.entitlement WHERE mask_method='unmask' AND role = current_role()) THEN val
  ELSE '********'
END;
Copy

在以前使用 ENCRYPTENCRYPT_RAW 加密的数据上使用 DECRYPT,并使用加密数据上的密码:

case
  when current_role() in ('ANALYST') then DECRYPT(val, $passphrase)
  else val -- shows encrypted value
end;
Copy

对 JSON 使用 <JavaScript UDF (VARIANT):

在此示例中,JavaScript UDF 掩码了 JSON 字符串中的位置数据。在 UDF 和掩码策略中,将数据类型设置为 VARIANT 非常重要。如果表列、UDF 和掩码策略签名中的数据类型不匹配,则 Snowflake 将返回错误消息,因为它无法解析 SQL。

-- Flatten the JSON data

create or replace table <table_name> (v variant) as
select value::variant
from @<table_name>,
  table(flatten(input => parse_json($1):stationLocation));

-- JavaScript UDF to mask latitude, longitude, and location data

CREATE OR REPLACE FUNCTION full_location_masking(v variant)
  RETURNS variant
  LANGUAGE JAVASCRIPT
  AS
  $$
    if ("latitude" in V) {
      V["latitude"] = "**latitudeMask**";
    }
    if ("longitude" in V) {
      V["longitude"] = "**longitudeMask**";
    }
    if ("location" in V) {
      V["location"] = "**locationMask**";
    }

    return V;
  $$;

  -- Grant UDF usage to ACCOUNTADMIN

  grant ownership on function FULL_LOCATION_MASKING(variant) to role accountadmin;

  -- Create a masking policy using JavaScript UDF

  create or replace masking policy json_location_mask as (val variant) returns variant ->
    CASE
      WHEN current_role() IN ('ANALYST') THEN val
      else full_location_masking(val)
      -- else object_insert(val, 'latitude', '**locationMask**', true) -- limited to one value at a time
    END;
Copy

使用 GEOGRAPHY 数据类型:

在此示例中,对于 CURRENT_ROLE 不是 ANALYST 的用户,掩码策略使用 TO_GEOGRAPHY 函数将列中的所有 GEOGRAPHY 数据转换为固定点,即 Snowflake 在加利福尼亚州圣马特奥市的经度和纬度。

create masking policy mask_geo_point as (val geography) returns geography ->
  case
    when current_role() IN ('ANALYST') then val
    else to_geography('POINT(-122.35 37.55)')
  end;
Copy

在数据类型为 GEOGRAPHY 的列上设置掩码策略,并将会话的 GEOGRAPHY_OUTPUT_FORMAT 值设置为 GeoJSON

alter table mydb.myschema.geography modify column b set masking policy mask_geo_point;
alter session set geography_output_format = 'GeoJSON';
use role public;
select * from mydb.myschema.geography;
Copy

Snowflake 返回以下结果:

---+--------------------+
 A |         B          |
---+--------------------+
 1 | {                  |
   |   "coordinates": [ |
   |     -122.35,       |
   |     37.55          |
   |   ],               |
   |   "type": "Point"  |
   | }                  |
 2 | {                  |
   |   "coordinates": [ |
   |     -122.35,       |
   |     37.55          |
   |   ],               |
   |   "type": "Point"  |
   | }                  |
---+--------------------+
Copy

B 列中的查询结果值取决于会话的 GEOGRAPHY_OUTPUT_FORMAT 参数值。例如,如果该参数值设置为 WKT,Snowflake 将返回以下结果:

alter session set geography_output_format = 'WKT';
select * from mydb.myschema.geography;

---+----------------------+
 A |         B            |
---+----------------------+
 1 | POINT(-122.35 37.55) |
 2 | POINT(-122.35 37.55) |
---+----------------------+
Copy

有关使用其他上下文函数和角色层次结构的示例,请参阅 高级列级安全主题

后续主题:

语言: 中文