访问控制是最容易理解的Web应用程序安全领域,但是在执行它们时必须采用合理、全面的方法。
首先应避免几种明显的缺陷。出现这些缺陷,通常是由于我们不了解执行有效访问控制应满足的基本要求,或者对用户应提出哪些请求,以及应用程序需要防御哪些威胁存在错误的认识。
不要认为用户不知道用于指定应用程序资源的URL或标识符(如账号和文档ID)就无法访问这些资源。假设用户知道每一个应用程序的URL和标识符,确保应用程序的访问控制足以防止未授权访问。
不要信任任何用户提交的表示访问权限的参数(如admin=true)。
不要认为用户将按设定的顺序访问应用程序页面。不要认为因为用户无法访问“编辑用户”页面,他们就不能到达由该页面链接的“编辑用户X”页面。
不要相信用户不会篡改通过客户端传送的数据。如果用户提交的一些数据已被确认,然后通过客户端传送,不要不经重新确认就相信传送的值。
以下是一些在Web应用程序中执行有效访问控制的最佳方法。
仔细评估并记录每个应用程序功能单元的访问控制要求。这包括谁能合法使用这些功能,以及用户通过这些功能能够访问哪些资源。
通过用户会话做出所有访问控制决定。
使用一个中央应用程序组件检查访问控制。
通过这个组件处理每一个客户端请求,确认允许提出请求的用户访问他请求的功能和资源。
使用编程技巧确保前面的方法没有例外。一种有效的方法是规定每个应用程序页面必须采用一个由中央访问控制机制查询的界面。强制开发者将访问控制逻辑代码写入每个页面,不得找借口省略这些代码。
对于特别敏感的功能,例如管理页面,可以通过IP地址进一步限制访问,确保只有特殊网络范围内的用户能够访问这些功能,不管他们是否登录。
如果静态内容需要得到保护,有两种方法可提供访问控制。首先,用户可通过向执行相关访问控制逻辑的服务器端动态页面传送一个文件名,间接访问静态文件。其次,可通过使用HTTP验证或应用程序服务器的其他特性隐藏进入的请求,并在允许访问前检查资源许可,控制用户直接访问静态文件。
无论何时通过客户端传送,指定用户所希望访问资源的标识符都容易遭到篡改。服务器应只信任完整的服务器端数据。任何时候通过客户端传送这些标识符,都需要对它们进行重新确认,以确保用户拥有访问被请求资源的授权。
对于安全性很关键的应用程序功能(如在银行应用程序中创建一个新的汇款收款人)考虑对每笔交易执行重复验证和双重授权,进一步确保该功能不会被未授权方使用。这样做还可以减轻其他可能的攻击(如会话劫持)造成的后果。
记录每一个访问敏感数据或执行敏感操作的事件。这些记录有助于检测并调查潜在的访问控制违反事件。
Web应用程序开发者通常逐步执行访问控制功能,在他们发现需要访问控制的每个页面插入代码,并在不同的页面间剪切和粘贴相同的代码以满足类似的需求。这种方法会在建立的访问控制机制中引入内在的缺陷:许多需要访问控制的情况被忽略;为一个区域设计的控制可能并不适用于另一个区域;在应用程序其他地方所做的修改可能会由于违反开发者做出的假设而与现有控制机制相互冲突。
与这种方法相比,前面描述的使用中央应用程序组件实施访问控制的方法具有诸多优点。
它可增进应用程序访问控制的透明度,使得不同开发者能够迅速理解其他人执行的控制机制。
它可提高访问控制的可维护性。许多变更只需要在一个共享的组件中应用一次即可,不需要将代码剪切并粘贴到多个位置。
它可改善可适应性。如果出现新的访问控制要求,这些要求可立即反映到由每个应用程序页面执行的一个现有API中。
它比在整个应用程序中逐步执行访问控制代码造成的错误和遗漏更少。
多层权限模型
与访问有关的问题不仅适用于Web应用程序,而且也适用于其中的其他基础设施,特别是应用程序服务器、数据库和操作系统。采取深层安全措施需要在上述每一个层面执行访问控制,建立几层保护。这样做可以强化对未授权访问威胁的防御,因为即使攻击者攻破一个层面的防御,也会被其他层面的防御机制阻止。
除上文所述的在Web应用程序中执行有效的访问控制外,还可以通过各种方式将这种多层次的方法应用于应用程序的基础组件中,举例如下。
可根据在应用程序服务器层面定义的用户角色,使用应用程序服务器对完整URL路径实施访问控制。
当执行其他用户的操作时,应用程序可使用一个不同的数据库账户。应为仅需查询(而非更新)数据的用户提供一个只读权限账户。
应使用一个权限表,对数据库中不同的数据库表执行严格的访问控制。
用于运行基础设施中每个组件的操作系统账户只需分配组件实际需要的最低权限。
复杂的安全性能关键的应用程序可通过一个定义应用程序不同用户角色和不同权限的矩阵来帮助实施这种多层防御措施。在每一个层面,应将不同的权限分配给每一个角色。图8-6是一个复杂应用程序的一部分权限矩阵。
我们可以在这种安全模型中应用各种有益的访问控制概念。
编程控制
(programmatic control)。数据库权限矩阵保存在一个数据库表中,并以编程的形式来做出访问控制决定。对用户角色进行分类可以简化某些访问控制检查,这一任务同样可以通过编程来完成。编程控制可能极其琐碎,并可能在应用程序中建立非常复杂的访问控制逻辑。
自主访问控制
(Discretionary Access Control,DAC)。使用自主访问控制,管理员可将自己的权限分配给其他与拥有特殊资源有关的用户。在封闭型DAC模型中,除非明确许可,否则拒绝访问。管理员还可以锁定或终止某个用户账户。在开放型DAC模型中,除非明确废除许可,否则允许访问。各种应用程序用户有权创建用户账户,并再次应用自主访问控制。
基于角色的访问控制
(Role-Based Access Control,RBAC)。这种控制使用许多命名的角色,每个角色拥有各不相同的特殊权限;每个用户分配有一个这样的角色。这样做可简化不同权限的分配与实施,并有助于管理复杂应用程序中的访问控制。使用角色对用户请求执行“前沿”访问检查有助于实行最少量的处理迅速拒绝许多未授权的请求。对特殊用户可访问的URL路径加以保护就是这种方法的一个典型应用。
当设计基于角色的访问控制机制时,我们有必要限制角色的数量,以对应用程序的权限进行有效管理。如果建立太多琐碎的角色,那么由于不同角色的数目繁多,可能就很难对其进行有效管理。如果建立太少的角色,这些角色就只能对访问进行粗略管理,个体用户分配到的权限将不足以履行他们的职能。
如果使用平台级控制、基于HTTP方法和URL限制对不同应用程序角色的访问,则应将这 些控制设计为使用默认拒绝模式,因为这是防火墙规则的最佳做法。这其中应包括各种特定规则,用于将某些HTTP方法和URL分配给特定角色,而且,随后的规则应拒绝不符合前一规则的任何请求。
声明式控制
(declarative control)。应用程序使用有限的数据库账户访问数据库。它对不同的用户群体使用不同的账户,每个账户分配到执行该群体所允许执行的操作所必需的最低权限。这种声明式控制从应用程序以外进行声明。这是深层防御原理的一个非常有用的应用,因为权限是由另外一个组件赋予应用程序的。这样,即使一名用户突破在应用程序层面执行的访问控制,企图实施添加新用户之类的敏感操作,他仍然会被阻止,因为他使用的数据库账户并未在数据库内获得必要的权限。
另一种情况是在配置应用程序的过程中,通过配置描述符文件(descriptor file)在应用程序服务器层面上应用声明式访问控制。但是,这种应用一般相对简单,并且无法进行扩展,所以无法管理大型应用程序中种类繁多的权限。