将访问权限存储在数据库表中是常见的做法吗?

Mic*_*cci 3 java rest authorization jax-rs access-control

对于基于角色的 RESTful API 访问,将访问权限存储在数据库表中是否是常见做法(并被认为是安全的)?我指的是用于限制用户访问某些端点、HTTP 方法(GET、POST、PUT、DELETE)或限制在查询中接受/返回哪些字段的访问权限。

或者访问权限是否更典型地存储在应用程序代码中(即,基于角色的逻辑编译到 API 本身中)?更一般地说,处理 RESTful API 的自定义访问权限的推荐方法是什么?

我不是指 API 本身的用户身份验证或基于 OAuth 的授权,这将通过标准的基于令牌的机制处理。

背景

我们正在 Java EE 7 中构建一个 RESTful API,它包括一个基于角色的访问控制机制,以确定在查询中接受/返回哪些端点、HTTP 方法和字段。在我们最初的设计中,我们在端点方法上使用容器请求过滤器读取的自定义注释来根据用户的角色确定用户的权限,并在(反)序列化过程中使用JsonView来过滤特定的实体字段。

该系统有效,但我们觉得它变得过于复杂且难以维护。我们已经讨论过将此授权信息移动到几个数据库表中,最重要的是一个存储表和列访问权限(PK 和 FK 列摘录)的表。

   AccessPermissions
========================
|  Column    |  Type   |
========================
| TableName  | varchar |
| ColumnName | varchar |
| HasCreate  | bit     |
| HasRead    | bit     |
| HasUpdate  | bit     |
| HasDelete  | bit     |
Run Code Online (Sandbox Code Playgroud)

每个角色(表未显示)可能会引用该AccessPermissions表中的许多行,以便端点方法访问和(反)序列化都可以通过询问用户是否具有允许请求访问的查询表和列名的条目来实现。

例如,POST /endpoint1with{"field1": "value", "field2": 23}将要求为用户分配一个角色,该角色在AccessPermissionswhereTableName = 'endpoint1'和中具有任何行HasCreate = true,并且请求正文中的 JSON 将仅反序列化所述行中的命名列。因此,如果AccessPermissions对应于该用户角色的唯一行是('endpoint1', 'field1', true, false, false, false)POST则将允许但部分 JSON 正文不会被反序列化(即,field2)。(此外,该用户可以POST /endpoint1但不能[GET|PUT|DELETE] /endpoint1。)

Dav*_*ard 5

通常,您希望尽可能多地将功能彼此分离。就像您不会在 API / 应用程序代码中紧密耦合(或重新实现)身份验证或登录一样,您希望保持授权分离。

实际上有一个名称:外化授权。Gartner 称其为外部化授权管理 (EAM)。有几种方法可以实现这一点。首先,您的开发框架可能为您提供了一种在 API 上定义授权的方法。Spring Security、.NET 声明就是这样的例子。

最重要的是,您有授权模型:

  • RBAC(基于角色的访问控制):在 RBAC 中,您拥有分配给资源的用户、角色和权限(有时称为权利)。例如,您可能拥有具有角色管理器的用户 Alice。该角色经理授予她 viewEndPoint1 作为权限。在某种程度上,它与您的建议非常相似。您的 REST 框架应该能够为您提供工具来实现这一点,例如通过注释。您可以在此处阅读一篇不错的文章。
  • ABAC(基于属性的访问控制):在 ABAC 中,您可以完全外部化来自应用程序的授权。这就是我每天都在做的事情,所以我可能有点偏见。在 ABAC 中,您有策略决策点 (PDP) 和策略执行点 (PEP) 的概念。后者是位于 API 前面的拦截器/API 网关/代理。PEP 拦截所有进出的流量,并询问 PDP 呼叫是否被授权。PDP 配置了一组基于属性的策略,这些策略说明什么可以发生,什么不能发生。这使您可以使用基于细粒度策略的授权方法。您不再需要创建自定义 RBAC 数据库架构。

想象一下您的 API 是关于保险合同和索赔的:

  • /极致/合同
  • /acme/contracts/{contractId}
  • /极致/索赔
  • /acme/claims/{claimId}
  • /极致/客户
  • /acme/customers/{custId}/contracts
  • /acme/customers/{custId}/claims

依此类推……您的政策可以说明:

  • 保险销售代表可以查看与他们相关的客户的合同。
  • 理赔处理员可以编辑其所在地区客户的理赔
  • 客户可以查看他们的所有合同和索赔。
  • 客户可以查看他们作为监护人的其他客户的合同。

如您所见,该策略定义了高级权限。这些源自您的业务应用程序的元数据(您已经拥有),例如合同 ID、合同价值、合同区域、合同客户;声明 ID,声明区域;客户指定的销售代表...

有几个框架可以实现这种方法。我最常使用的是 XACML(可扩展访问控制标记语言)。

它为您的 API 安全性带来的一些主要好处是:

  • 细粒度访问:您可以使用风险、上下文和关系
  • 完全与应用程序逻辑分离 - 摆脱自定义代码和数据库模式
  • 可扩展:您可以在需要时添加新场景。因此,如果您将来有新的 API,只需添加新策略即可。
  • 可审计性和问责制:与编码相比,您可以更轻松地审计策略;您可以证明谁(试图)访问(编辑)API 以及该访问是被授予还是被拒绝
  • 过滤:可以过滤返回的数据

看看这个关于 API 安全性的报告免责声明:我为写那篇文章的公司工作)。

  • 谢谢(你的)信息!我会通读你发送的链接。我有很多东西要学。但是,如果我的理解正确,您是在建议我们的 REST API 不应该处理访问控制本身。这应该被外化。我可以想象这对于我们已经在使用注释进行的端点和方法如何工作得很好,但是对特定字段的访问呢?此外,我们觉得基于注释的方法与将访问控制矩阵保存在表中相比更难维护,表中将通过注释进行检查。缺点是什么? (2认同)