范围过滤:通往RBAC的实用桥梁

本文介绍了一种实用的范围过滤方法,可在不进行大规模架构改造的情况下实现基于角色的访问控制。通过API层过滤和映射表,为现有系统提供RBAC功能,是向原生行级安全策略过渡的理想中间方案。

范围过滤:通往RBAC的实用桥梁

需要RBAC但无法承担架构改造的成本?范围过滤让您在API级别实施访问控制,轻松将其叠加到现有系统上。

引言

作为一家刚完成A轮融资的公司,我理解优先事项是让产品正常运行。在我们案例中,这意味着先展示数据洞察解决方案的有效性,然后再实施复杂的控制措施,如基于角色的访问控制(RBAC)。

但现在时机已到。客户正在入驻,您需要确保只有正确的人员才能访问正确的客户数据。

RBAC解决方案并不缺乏,问题在于大多数方案需要对现有代码库进行大规模更改,特别是在API方面。如果您的API由Snowflake提供支持,Snowflake的行访问策略(RAPs)提供了出色的原生RBAC机制,但采用它们通常意味着破坏查询、重写视图和重新架构访问逻辑。

如果您的团队尚未准备好进行这种级别的更改,有一个折中方案。您可以使用映射表实现基于范围的过滤,完全在SQL中模拟RBAC,而无需修改底层数据模型。这种方法在API级别实施RBAC,以最小干扰包裹现有端点,为团队在全面推出RAP之前提供喘息空间。

RBAC简介

数据安全不仅关乎谁能进入,更关乎进入后能看到什么。随着系统变得越来越复杂,仅问"谁有访问权限"已经不够。真正的问题是:“谁有权访问数据的哪个部分?”

这就是基于角色的访问控制(RBAC)的用武之地。RBAC不直接向用户分配权限,而是向角色分配权限,用户根据分配的角色继承访问权限。这是一个广泛采用的模型,可简化大规模访问控制。

当原生行级安全成为瓶颈

原生行级安全通常复杂、灵活,并设计用于支持各种用例。正确实施后,它消除了下游访问控制的需求,数据库本身返回用户有权查看的行。

以Snowflake的行访问策略(RAPs)为例。RAPs通过在表和视图上直接定义访问规则,提供平台级RBAC。一旦应用,Snowflake会根据您指定的条件自动执行这些策略。这意味着查询仅返回用户允许查看的行,无需在API或应用层进行额外过滤。

这是一个强大的系统,但早期采用会带来我们团队难以接受的权衡:

  • 高操作开销:需要自定义代码将RAPs应用于所有相关表和视图,并自动应用于任何新增对象
  • 风险的全局副作用:RAPs是模式级更改,如果错误应用,开发人员和分析师可能会静默失去对行的访问权限

范围过滤:通往RBAC的轻量级桥梁

当需要RBAC但无法承担全面平台改造时,该怎么办?答案是使用范围过滤。

核心思想很简单:应用程序UI随每个请求发送一个范围,明确定义用户有权访问的数据。然后,后端使用此范围来过滤API返回的数据。

在后端,您维护一个映射表,定义哪些范围可以访问哪些数据。数据源无关紧要,可以是Snowflake账户、AWS区域或Databricks工作区。在我们的案例中,我们将数据存储在由account_id和region组合唯一标识的账户中。因此,我们的映射表将范围链接到(account_id, region)对。

一旦有了这个映射,实现RBAC就变得像用过滤层包裹现有API一样简单。此包装器查找用户的范围,获取允许的(account_id, region)组合,并将它们作为过滤器应用于查询,所有这些都无需修改底层数据模型或视图。

范围过滤实施步骤

1. 定义和传递范围

UI必须发送代表用户访问级别的"范围"。范围可以是团队、角色,或者仅仅是用于访问控制的字符串标识符,可以映射到各种数据账户。

此范围随API请求一起发送,要么作为HTTPS标头,要么在请求的查询参数中。

为了额外安全,范围应成为API合同的永久组成部分,这样当未发送范围时,不显示任何数据。

2. 创建映射表

映射表定义了范围与它们被允许访问的数据分区之间的桥梁。您可以将其存储在后端易于查询的任何位置,我们使用了Snowflake表。

表的列可以包括Scope、Account_ID和Region。Scope可以是字符串列表。

映射应支持:

  • 每个账户多个范围
  • 每个范围多个账户
  • [可选] 按范围快速查询

3. 创建RBAC包装器

API包装器如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
WITH BASE_QUERY AS (
    -- 您的原始查询放在这里
    %[1]s
),

ACCOUNTS_RBAC AS (
    -- 为用户范围选择允许的账户
    %[2]s
)

SELECT
    BASE_QUERY.*
    EXCLUDE RBAC_IDENTIFIER
FROM
    BASE_QUERY
INNER JOIN ACCOUNTS_RBAC
    ON
        BASE_QUERY.RBAC_IDENTIFIER:ACCOUNTID = ACCOUNTS_RBAC.ACCOUNTID
        AND
        BASE_QUERY.RBAC_IDENTIFIER:REGION = ACCOUNTS_RBAC.REGION

BASE_QUERY CTE包含您的原始查询逻辑,这是未经过滤的API级数据请求。对此的唯一编辑是发回RBAC_IDENTIFIER,指示数据行的账户、区域或任何其他标识符。

ACCOUNTS_RBAC CTE根据用户的范围从映射表中查找,拉取授权的(account_id, region)对列表。

最终的SELECT将两者在这些标识符上连接,仅返回用户允许查看的行。

我们排除RBAC_IDENTIFIER以防止泄露内部RBAC元数据。

ACCOUNTS_RBAC CTE可能如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
SELECT DISTINCT
    ACCOUNTID,
    REGION
FROM ACCOUNTS
WHERE
    'ROOT' IN (%[1]s)
    OR ARRAYS_OVERLAP(ACCOUNTS.SCOPES, [%[1]s])
UNION DISTINCT
SELECT DISTINCT
    ACCOUNTS.ACCOUNTID,
    'aws-region-agnostic' AS REGION
FROM ACCOUNTS
WHERE
    'ROOT' IN (%[1]s)
    OR ARRAYS_OVERLAP(ACCOUNTS.SCOPES, [%[1]s])

这里需要考虑两点:

  • 拥有访问一切的ROOT范围
  • 处理区域不可知边缘情况

就是这样!

通过用此范围过滤层包装您的API:

  • 避免重新架构数据平台
  • 获得清晰、可审计的RBAC实施
  • 保留未来迁移到原生解决方案(如Snowflake RAPs)的灵活性

实际考虑因素

范围过滤简单、灵活且易于改造到现有系统中,但像任何访问控制模型一样,其实际应用伴随着权衡和操作细节。

1. 它仍然是集中逻辑,因此要小心处理

虽然它不是像Snowflake RAP那样的全局策略,但它仍然是中央看门人。任何关于连接、错误范围等逻辑的错误仍可能导致未经授权的访问或意外缺失行!

边缘案例测试和单元测试可以帮助解决这些问题。

2. 范围生命周期管理可能很棘手

随着时间的推移,范围可能变得难以管理,团队可能会拆分并需要子范围,可能会要求动态范围等,由于我们的映射表是手动映射的,这可能难以管理。

3. 性能考虑

最终,我们运行的是SQL查询,其成本将基于映射表的大小而定。连接可能很昂贵,数据可能是多区域的。

缓存可能是解决此问题的可能方案。索引也可能有所帮助。

结论

RBAC是任何现代数据平台的重要组成部分。但将其引入现有代码库并不总是需要全面的架构改造。

范围过滤提供了一个实用的桥梁,一个轻量级的API级实施机制,实施简单、易于调试,并且足够灵活,可以支持您的产品,直到可以采用像Snowflake行访问策略这样的原生RBAC解决方案。它允许团队在保持安全的同时快速移动。

从小处开始,随时间构建,在可能时加强。范围过滤是一个很好的起点。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计