使用安全 UDFs 和存储过程保护敏感信息

为了帮助确保对不应访问敏感信息的用户隐藏敏感信息,可以在创建用户定义函数 (UDF) 和存储过程时使用 SECURE 关键字。

本主题介绍如何:

本主题内容:

限制 UDF 或过程定义的可见性

对于 UDF 或存储过程,可以阻止用户查看定义细节。如果指定 UDF 或过程是安全的,则这些详细信息仅对授权用户可见,换言之,仅对被授予拥有该函数的角色的用户可见。

例如,对于安全的函数或过程,为未经授权的用户省略的信息包括:

  • 主体(包含其逻辑的处理程序代码)

  • 导入列表

  • 处理程序名称

  • 包列表

未经授权的用户仍将能够看到一些信息,包括函数或过程的:

  • 参数类型

  • 返回类型

  • 处理程序语言

  • null 处理

  • 波动性

有关授予角色的更多信息,请参阅 GRANT ROLE访问控制概述

对于安全的函数或过程,未经授权的用户(未被授予拥有该函数或过程的角色的用户)在使用以下任何一项时,可能无法查看函数或过程的定义:

请注意,对于处理程序是用 Java、Python 或 Scala 编写的函数和过程,它们允许使用从 Snowflake 暂存区导入代码或数据文件的 IMPORTS 子句。使用 SECURE 关键字*不会*对这些暂存区的可见性或访问产生任何影响。

此外,对于处理程序是用 Java、Python 或 Scala 编写的函数和过程,使它们变得安全可确保它们在单独的沙箱中执行,从而不会在它们之间共享资源。

有关使用 SECURE 关键字的更多信息,请参阅 创建安全的 UDF 或存储过程

限制 UDF 敏感数据的可见性

在 UDFs 中,可以通过使 UDF 变得安全来防止用户看到应隐藏的数据。为此,请在创建或更改 UDF 时使用 SECURE 关键字。

如果为了确保数据隐私而特别指定 UDF(换句话说,限制对敏感数据的访问,这些数据不应向基础表的所有用户公开),应将其定义为安全函数。

如果是为方便查询而定义 UDF(例如,为了简化数据查询而创建此类函数,以便用户无需了解基础数据表示形式),则 应使其变得安全。这是因为,Snowflake 查询优化器在评估安全的 UDFs 时会绕过用于常规 UDFs 的优化手段。这可能会降低安全 UDFs 的查询性能。

要限制对 UDF 基础数据的可见性,请在创建或更改此类函数时使用 SECURE 关键字。有关更多信息,请参阅 创建安全的 UDF 或存储过程

数据如何变得可见

一些针对 UDFs 的内部优化手段(包括称为 下推 的优化手段)需要访问基表中的基础数据。此访问可能允许通过编程方法间接地公开对 UDF 用户隐藏的数据。在某些情况下,用户可能可以推断出有关用户无法直接看到的行的信息。

安全的 UDFs 不使用这些优化手段,从而确保用户甚至无法间接访问基础数据。有关下推的更多信息,请参阅:doc:/developer-guide/pushdown-optimization

小技巧

在决定是否使用安全的 UDF 时,应考虑 UDF 的用途,并权衡数据隐私/安全性与查询性能之间的利弊。

此外,如果数据足够敏感,以至于您断定通过某种类型的对象(如 UDFs)进行访问应该是安全的,则应强烈考虑确保通过其他类型的对象(如视图)进行的访问也是安全的。

例如,如果只允许安全的 UDFs 访问给定表,则允许访问同一个表的任何视图可能也应是安全的。

安全的 UDFs 如何保护数据

下推优化和数据可见性 中所述,下推优化可以对确定查询处理方式的筛选器进行重新排序。在重新排序的过程中,如果此优化允许先运行常规筛选器,然后再应用那些用于保护数据的相应筛选器,则可能会公开基础详细信息。因此,解决方案是阻止优化器下推某些类型的筛选器(更一般地说,阻止优化器使用某些类型的优化,包括但不限于筛选器下推),如果这些优化不安全的话。

将 UDF 声明为“安全”会告诉优化器不要下推某些筛选器(更一般地说,不要使用某些优化)。但是,阻止某些类型的优化可能会影响性能。

保护敏感数据访问的最佳实践

安全的 UDFs 可防止用户可能接触到由函数筛选的表行中的数据。但是,如果未仔细构造 UDFs,数据所有者仍可能会无意中公开有关基础数据的信息。本部分介绍一些需要避免的潜在陷阱。

避免公开序列生成的列值

生成代理项键的常见做法是使用序列或自动递增列。如果这些密钥向无权访问所有基础数据的用户公开,则用户可能能够猜测基础数据分布的详细信息。

例如,假设我们有一个公开 ID 列的 get_widgets_function() 函数。如果 ID 是从序列生成的,则 get_widgets_function() 的用户可以推断出在用户有权访问的两个小组件的创建时间戳之间创建的小组件总数。请考虑以下查询和结果:

select * from table(get_widgets_function()) order by created_on;

------+-----------------------+-------+-------+-------------------------------+
  ID  |         NAME          | COLOR | PRICE |          CREATED_ON           |
------+-----------------------+-------+-------+-------------------------------+
...
 315  | Small round widget    | Red   | 1     | 2017-01-07 15:22:14.810 -0700 |
 1455 | Small cylinder widget | Blue  | 2     | 2017-01-15 03:00:12.106 -0700 |
...
Copy

根据结果,用户可能会怀疑在 1 月 7 日至 1 月 15 日期间创建了 1139 个小组件 (1455 - 315)。如果此信息过于敏感而无法向函数用户公开,则可以使用以下任一替代方法:

  • 不要将序列生成的列作为函数的一部分公开。

  • 使用随机标识符(例如由 UUID_STRING 生成的标识符)而不是序列生成的值。

  • 以编程方式对标识符进行模糊处理。

限制对扫描数据大小的可见性

对于包含安全函数的查询,Snowflake 不会公开扫描的数据量(以字节或微分区为单位)或数据总量。这是为了防止仅有权访问部分数据的用户看到这些信息。

但是,用户可能仍能够根据查询的性能特征对基础数据的数量进行观察。例如,运行两倍时间的查询可能会处理两倍的数据。虽然任何此类观察充其量都是近似的,但在某些情况下,即使是这种级别的信息也可能不希望被公开。

在这种情况下,应明确指定每个用户/角色可访问的数据,而不是向用户公开有关基础数据的函数。对于本主题中描述的 widgets 表,将为有权访问小组件的每个角色创建一个表。这些表中的每一个将仅包含该角色可访问的小组件,并且将向角色授予对其表的访问权限。这比使用单个函数要麻烦得多,但对于安全性极高的情况,这可能是必要的。

授权特定账户中的用户访问基表

将安全的 UDFs 与 :doc:` 数据共享 </user-guide/data-sharing-gs>` 结合使用时,CURRENT_ACCOUNT 函数可用于授权特定账户中的用户访问基表中的行。

备注

CURRENT_ROLECURRENT_USER 函数与将和 Snowflake 账户共享的安全 UDFs 一起使用时,Snowflake 会为这两个函数返回 NULL 值。原因是,共享的数据的所有者通常并不控制与之共享 UDF 的账户中的用户或角色。

安全的 UDFs 和掩码策略

如果在 掩码策略 中使用 UDF(无论此 UDF 是否是安全的 UDF),请确保列的数据类型、UDF 和掩码策略都匹配。

有关更多信息,请参阅 掩码策略中的用户定义函数

创建安全的 UDF 或存储过程

创建或更改 UDF 或过程时,可以使用 SECURE 关键字使它们变得安全。

要创建或转换 UDF 以确保其安全,请在使用以下命令时指定 SECURE:

要创建过程以确保其安全,请在使用以下命令时指定 SECURE:

确定 UDF 或过程是否安全

可以使用 SHOW FUNCTIONS 或 SHOW PROCEDURES 命令确定函数或过程是否安全。这两个命令返回一个包含 IS_SECURE 列的表,此列的值为 Y 表示安全,为 N 则表示不安全。

以下示例中的代码返回 MYFUNCTION 函数的属性表。

show functions like 'MYFUNCTION';
Copy

在查询配置文件中查看安全函数的详细信息

安全函数的内部结构不会在 :doc:`查询配置文件 </user-guide/ui-query-profile>`(在 Web 界面中)中公开。即使对于安全函数的所有者也是如此,因为非所有者可能有权访问所有者的查询配置文件。

语言: 中文