参考文章The New RBAC: Resource-Based Access Control

什么是角色?

在谈到应用程序安全性时,大多数人都对现有的角色概念感到满意。角色通常是代表一组行为或责任的命名实体。这些行为会转化成您是否可以使用软件应用程序执行的操作。角色通常分配给用户帐户,因此通过关联,用户可以“执行”属于不同角色的操作。

例如,如果用户登录到某个应用程序,并且他们的帐户已被分配了“项目经理”角色,那么这个假设就是用户可以“做”项目经理应该能够对应用程序做的所有事情——列出可用的应用程序、向项目添加或删除员工、生成项目报告等。

从这种意义上讲,角色主要是一个行为概念:角色可以让您了解到用户可以在应用程序中执行何种操作。

基于角色的访问控制

由于角色主要是行为概念,因此开发软件时的逻辑步骤是使用角色作为控制对应用程序功能或数据的访问的手段。正如您所料,大多数人将此方法称为基于角色的访问控制,或简称为RBAC。

但是,实际实现和执行访问控制有两种主要方式:隐式方式和显式方式。

今天绝大多数软件应用程序都使用隐式访问控制。我认为显式访问控制对于保护当今的软件应用程序要好得多。

隐式访问控制

如前所述,角色代表行为或责任。但是,我们如何准确地知道与角色相关的行为或责任?

答案是,对于绝大多数应用程序,您并不确切知道该角色代表什么。您当然有个好主意 – 您知道具有“管理员”角色的人可能会锁定用户帐户或配置应用程序的某些部分,并且分配了“客户”角色的用户帐户可以将项目放入购物车或请求新产品。但通常没有什么能够具体地定义这些行为是什么。

以字符串“Project Manager”为例。它只是一个字符串名称 – 软件程序可以检查以找出“基于此名称,我知道分配了此角色的用户可以执行X,Y和Z”。开发人员通常仅使用此名称对其应用程序进行编程 例如,要查看是否允许用户查看项目报告,您通常会看到如下代码:

//Listing 1. Example Implicit Role-Based Access Control security check:
if (user.hasRole("Project Manager") ) {
    //show the project report button
} else {
    //don't show the button
}

在此示例代码块中,软件开发人员根据“项目经理”角色(可能是项目要求)决定是否显示按钮。但请注意,上面代码中的任何内容实际上都没有明确说明“允许项目经理角色查看项目报告”。没有人在软件中的任何地方定义行为声明 – 暗示 “项目经理”用户可以查看项目报告,因此开发人员编写反映该假设的if/else语句。

脆弱的安全政策

像上面这个例子那样的安全访问控制非常脆弱。也就是说,即使安全要求稍有变化,它也可能会破坏,失败或导致效率低下。

为了说明,让我们假设编写软件的团队被告知“哦,顺便说一句,我们需要一个新的’部门经理’角色,他们还需要能够查看项目报告。实现它”。

现在,软件开发人员需要回到代码中将其更改为:

//Listing 2. Example Modified Implicit Role-Based Access Control security check:
if (user.hasRole("Project Manager") || user.hasRole("Department Manager") ) {
    //show the project report button
} else {
    //don't show the button
}

然后,开发人员需要更新他的测试用例,重新构建软件,完成任何QA流程,并安排重新部署到生产 – 所有这些都是因为相对简单的新安全要求。

但是,如果管理层返回并要求另一个角色能够查看报告,会发生什么?或者如果他们以后需要删除该功能?

或者,如果软件需要支持在运行时动态创建或删除角色的能力,因为他们希望客户能够自己配置角色,该怎么办?

在任何这些方案中,常见的隐式(静态字符串)基于角色的访问控制方法无法满足安全需求。如果安全策略发生变化,如果您根本不需要触摸源代码,那将是理想的选择。理想的是,只有在数据模型要求认为必要时才需要更改源代码。

显式访问控制:更好的方法

如上所述,使用隐式访问控制方法更改安全策略可能会对软件开发造成严重影响。如果安全策略更改不会强制执行代码重构,那将会更好。理想情况下,如果在应用程序运行时可以更改安全策略,那么这样做真的很好,这样您就不会影响最终用户。这对于安全性来说甚至更好,因为如果您发现错误或危险(或者您犯了政策错误),您可以快速将策略更改为正确的配置,并且软件仍可正常运行。

那么我们如何才能实现这些好处呢?我们可以通过明确指出在应用程序中实际可以完成的操作来做到这一点。但那究竟是什么意思呢?

当您查看上面的隐式访问控制检查时,他们真正想要做什么?执行的最基本控制是什么?

从根本上说,这些检查试图保护资源(项目报告)以及用户可以对这些资源采取的行动(例如查看/阅读它们)。当您将其分解为最原始的级别时,您可以开始以更细粒度(和更改弹性)的方式描述安全策略。

例如,让我们改变上面的代码块,以便用基于资源的语义有效地做同样的事情:

//Listing 3. Example Explicit Access Control security check:
if (user.isPermitted("projectReport:view:12345")) {
    //show the project report button
} else {
    //don't show the button
}

此示例更明确地说明正在控制哪些访问。冒号分隔的语法在这里并不重要 – 它只是一个例子。更重要的是,我们基本上检查“如果当前用户被允许“查看”具有ID“12345”的“projectReport”,则显示项目报告按钮。也就是说,我们明确地使用用户帐户归因于特定资源实例的具体行为声明。

为什么这种方式更好?

上面这个最新的例子有一个微妙但非常重要的区别:代码现在基于受保护的内容,而不是谁可能有能力。虽然在概念上显然很简单(也许更明显),但这对开发和生产部署实践有重大影响:

  • 减少代码重构:通过对基础代码是什么 -该应用程序与之交互的资源的应用能做到的,我们正在事情是核心应用程序本身和改变更频繁立足我们的安全。使用这种方法,软件开发人员可以在处理应用程序功能时修改安全检查 – 而不是像通常需要的隐式RBAC那样。

  • 资源和操作很直观:表示受保护的内容以及如何对其进行操作是一种更自然的思考问题的方式。面向对象的编程范例和REST通信模型从根本上反映了这一观点,并因此而非常成功。

  • 灵活的安全模型:上面的代码示例并未规定如何允许用户,组或角色对资源执行操作。这意味着可以支持任何安全模型设计。例如,可以将行为(权限)直接分配给用户。或者也许可以将它们分配给角色,角色又分配给用户。也许存在与角色等相关联的群体概念。可能性是开放的,可以根据您的应用进行定制。

  • 外部化安全策略管理:由于源代码仅反映资源和行为,而不反映用户,组和角色的组合,因此可以将这种关联管理外部化到代码的不同部分或专用工具或管理控制台。这意味着开发人员不需要花时间进行安全策略更改,而是业务分析师甚至最终用户可以根据需要更改安全策略。

  • 在运行时进行更改:由于安全代码检查不依赖于了解行为的关联方式(即组,角色和用户可能如何相关),因此您可以在应用程序运行时更改应用程序安全策略中的这些关联。没有必要重构代码来强制执行新的安全策略更改,就像隐式RBAC一样。

RBAC新解: 基于资源-的访问控制

除了上面列出的好处之外,我还应该重申这种显性机制所提供的灵活安全模型的概念。

如果要保留或模拟基于角色的访问控制的传统概念,可以根据需要将行为(权限)直接分配给角色。从这个意义上讲,您仍然可以使用基于角色的访问控制安全策略 – 只是您将拥有显式RBAC策略而不是传统的隐式策略。

但这引出了一个问题 – 为什么要停止角色?您可以将行为直接分配给用户,组,或安全策略可能允许的任何其他内容。

从这个意义上讲,基于上面基于资源的显式访问控制的好处,RBAC可能具有新的含义:“基于资源的访问控制”。只是一个想法……

现实世界的例子:Apache Shiro

如果您对如何在数千个实际应用程序中使用的真实框架中建模这一点感到好奇,请查看Apache Shiro,这是一个用于保护JVM平台上的应用程序的现代框架。Shiro通过其权限概念支持基于资源的访问控制。

当然,您不需要使用Shiro从本文中提到的想法中受益,但如果您正在寻找所讨论内容的想法或具体示例,那么它可能会有所帮助。