Web 应用程序安全设计指南
本页内容
本模块内容 | |
目标 | |
适用范围 | |
如何使用本模块 | |
Web 应用程序的体系结构和设计问题 | |
部署考虑 | |
输入验证 | |
身份验证 | |
授权 | |
配置管理 | |
敏感数据 | |
会话管理 | |
加密 | |
参数操作 | |
异常管理 | |
审核和记录 | |
设计指南小结 | |
总结 | |
其他资源 |
本模块内容
Web 应用程序为结构设计人员、设计人员和开发人员提出了一系列复杂的安全问题。最安全、最有能力抵御攻击的 Web 应用程序是那些应用安全思想构建的应用程序。
在设计初始阶段,应该使用可靠的体系结构和设计方法,同时要结合考虑程序部署以及企业的安全策略。如果不能做到这一点,将导致在现有基础结构上部署应用程序时,要不可避免地危及安全性。
本模块提供了一系列安全的体系结构和设计指南,并按照常见的应用程序漏洞类别进行了组织。这些指南是 Web 应用程序安全的重要方面,并且是经常发生错误的领域。
目标
使用本模块可以实现:
• |
确定安全 Web 应用程序的重要体系结构和设计问题。 |
• |
设计时考虑重要部署问题。 |
• |
制定能增强 Web 应用程序输入验证的策略。 |
• |
设计安全的身份验证和会话管理机制。 |
• |
选择适当的授权模型。 |
• |
实现有效的帐户管理方法,并保护用户会话。 |
• |
对隐私、认可、防止篡改和身份验证信息进行加密。 |
• |
防止参数操作。 |
• |
设计审核和记录策略。 |
适用范围
虽然本模块内容包含在 ASP.NET 安全手册中,但其适用于对开发安全 Web 应用程序感兴趣的所有人员。
如何使用本模块
本模块主要提供了设计应用程序时应该遵循的一些指南和原则。
为了充分理解本模块内容,请:
• |
了解应用程序将会受到的威胁,以确保通过程序设计解决这些问题。阅读模块 2 威胁和对策,以了解需要考虑的威胁。模块 2 列出了可能会危害应用程序的因素。在程序设计阶段应该考虑到这些威胁。 |
• |
在应用程序易受攻击的重要环节应用系统的方法。将重点放在程序部署、输入验证、身份验证和授权、加密及数据敏感度、配置、会话、异常管理以及适当的审核和记录策略上,以确保应用程序具有责任性。 |
Web 应用程序的体系结构和设计问题
Web 应用程序向设计人员和开发人员提出了许多挑战。HTTP 是无国界的,这意味着跟踪每位用户的会话状态将成为应用程序的责任。作为先导者,应用程序必须能够通过某种形式的身份验证来识别用户。由于所有后续授权决策都要基于用户的标识,因此,身份验证过程必须是安全的,同样必须很好地保护用于跟踪已验证用户的会话处理机制。设计安全的身份验证和会话管理机制仅仅是 Web 应用程序设计人员和开发人员所面临的众多问题中的两个方面。由于输入和输出数据要在公共网络上进行传输,因此还会存在其他挑战。防止参数操作和敏感数据泄漏是另外一些重要问题。
图 4.1 列出了安全设计方法中必须解决的其他重要问题。
图 4.1
Web 应用程序设计问题
本模块中的设计指南是根据应用程序漏洞类别进行组织的。实际经验表明,如果这些领域的设计存在薄弱环节,将会导致安全漏洞。表 4.1 列出了漏洞的类别,每个类别都突出显示了由于设计不当可能会导致的潜在问题。
表 4.1:由于设计不当导致的 Web 应用程序漏洞及潜在问题
漏洞类别 | 由于设计不当而引起的潜在问题 |
输入验证 |
嵌入到查询字符串、表单字段、cookie 和 HTTP 头中的恶意字符串的攻击。这些攻击包括命令执行、跨站点脚本(XSS)、SQL 注入和缓冲区溢出攻击。 |
身份验证 |
标识欺骗、密码破解、特权提升和未经授权的访问。 |
授权 |
访问保密数据或受限数据、篡改数据以及执行未经授权的操作。 |
配置管理 |
对管理界面进行未经授权的访问、具有更新配置数据的能力以及对用户帐户和帐户配置文件进行未经授权的访问。 |
敏感数据 |
泄露保密信息以及篡改数据。 |
会话管理 |
捕捉会话标识符,从而导致会话劫持及标识欺骗。 |
加密 |
访问保密数据或帐户凭据,或二者均能访问。 |
参数操作 |
|
异常管理 |
拒绝服务和敏感的系统级详细信息的泄漏。 |
审核和记录 |
不能发现入侵迹象、不能验证用户操作,以及在诊断问题时出现困难。 |
部署考虑
在应用程序设计阶段,应考虑公司安全策略和程序,以及部署应用程序的基础结构。通常,目标环境是固定不变的,应用程序的设计必须要反映这些限制条件。有时需要折衷考虑设计方案,例如,由于存在协议和端口限制,或是特定部署拓扑结构的要求。要在设计初期确定存在哪些限制条件,以避免日后在开发过程中出现意外;另外,应邀请网络和基础结构工作组的成员参与此过程。
图 4.2 显示了需在程序设计阶段考虑的几个程序部署问题。
图 4.2
部署考虑
安全策略和程序
安全策略确定允许应用程序及其用户执行哪些操作。更重要的是,安全策略定义了一些限制,以确定不允许应用程序及其用户执行哪些操作。设计应用程序时,应在公司安全策略定义的框架内进行标识和工作,以确保您没有违反防止部署应用程序的策略。
网络基础结构组件
确保您了解目标环境提供的网络结构,并了解网络的基本安全要求,如筛选规则、端口限制、支持的协议等等。
确定防火墙和防火墙策略可能会如何影响应用程序的设计和部署。在面向 Internet 的应用程序和内部网络之间可能存在防火墙将其隔开。也许还存在用于保护数据库的其他防火墙。这些防火墙影响了可用的通信端口,因此会影响 Web 服务器到远程应用程序和数据库服务器的身份验证选项。例如,Windows 身份验证需要附加端口。
在设计阶段,需要考虑允许哪些协议、端口和服务从外围网络中的 Web 服务器访问内部资源。还应确定应用程序设计所需的协议和端口,并分析打开新端口或使用新协议会带来哪些潜在威胁。
交流并记录所有有关网络和应用层安全的设想,以及哪些组件将处理哪些问题。这样,当开发人员和网络管理人员都认为对方会解决安全问题时,可以防止安全控制失败。注意网络为应用程序提供的安全防范措施。设想如果更改网络设置,可能会带来哪些安全隐患。如果实现特定的网络结构更改,将会出现多少安全漏洞?
部署拓扑结构
应用程序的部署拓扑结构和是否具有远程应用层是设计阶段必须考虑的关键问题。如果具有远程应用层,需要考虑怎样保护服务器之间的网络以减少网络窃听威胁,以及怎样保护敏感数据的保密性和完整性。
此外,还要考虑标识符流,并确定在应用程序连接到远程服务器时将用于网络身份验证的帐户。一种常见方法是使用最小特权进程帐户,并在远程服务器上创建一个具有相同密码的帐户副本(镜像)。另一种方法是使用域进程帐户,此类帐户管理方便,但会带来更大的安全问题,因为很难限制该帐户在网络上的使用。未建立信任关系的介入防火墙和单独域使应用本地帐户成为唯一的选择。
Intranet、Extranet 和 Internet
Intranet、Extranet 和 Internet 应用程序方案在设计上都具有挑战性。应该考虑的问题包括:怎样将调用方标识通过多重应用层传送到后端资源?在哪里进行身份验证?能否信任前端的身份验证,然后使用可信连接访问后端资源?在 Extranet 方案中,还必须考虑是否信任合作伙伴帐户?
有关这些问题以及其他方案特定问题的详细信息,请参阅“Building Secure ASP.NET Web Applications:Authentication, Authorization, and Secure Communication”中的“Intranet Security”、“Extranet Security”和“Internet Security”部分,Authentication, Authorization, and Secure Communication”,其网址为:http://msdn.microsoft.com/library/en-us/dnnetsec/html/secnetlpMSDN.asp(英文)。
输入验证
输入验证是一个很复杂的问题,并且是应用程序开发人员需要解决的首要问题。然而,正确的输入验证是防御目前应用程序攻击的最有效方法之一。正确的输入验证是防止 XSS、SQL 注入、缓冲区溢出和其他输入攻击的有效对策。
输入验证非常复杂,因为对于应用程序之间甚至应用程序内部的输入,其有效构成没有一个统一的答案。同样,对于恶意的输入也没有一个统一的定义。更困难的是,应用程序对如何处理此输入将会影响应用的风险。例如,您是否存储用于其他应用程序的数据,或者应用程序是否接受来自其他应用程序所创建的数据源的输入?
以下做法可以增强 Web 应用程序的输入验证:
• |
假定所有输入都是恶意的。 |
• |
集中方法。 |
• |
不要依赖客户端验证。 |
• |
注意标准化问题。 |
• |
限制、拒绝和净化输入。 |
假定所有输入都是恶意的
开始输入验证时,首先假定所有输入都是恶意的,除非有证据表明它们并无恶意。无论输入是来自服务、共享文件、用户还是数据库,只要其来源不在可信任的范围之内,就应对输入进行验证。例如,如果调用返回字符串的外部 Web 服务,如何知道该服务不会执行恶意命令呢?另外,如果几个应用程序写入同一个共享数据库,您在读取数据时,如何知道该数据是否安全呢?
集中方法
将输入验证策略作为应用程序设计的核心元素。考虑集中式验证方法,例如,通过使用共享库中的公共验证和筛选代码。这可确保验证规则应用的一致性。此外,还能减少开发的工作量,且有助于以后的维护工作。
许多情况下,不同的字段要求不同的验证方法,例如,要求使用专门开发的常规表达式。但是,通常可以得到验证常用字段(如电子邮件地址、标题、名称、包括邮政编码在内的通讯地址,等等)的常规方法。图 4.3 显示了此验证方法。
图 4.3
输入验证的集中式方法
不要依赖于客户端验证
应使用服务器端代码执行其自身的验证。如果攻击者绕过客户端或关闭客户端脚本例程(例如,通过禁用 JavaScript 脚本),后果如何?使用客户端验证可以减少客户端到服务器端的往返次数,但不要依赖这种方法进行安全验证。这是一个深入彻底的防御示例。
注意标准化问题
数据的标准形式是最标准、最简单的形式。标准化是指将数据转化为标准形式的过程。文件路径和 URL 尤其倾向于标准化问题,许多广为人知的漏洞利用都直接源自标准化缺陷。例如,请考虑以下字符串,它以标准形式表示了文件及其路径。
c:/temp/somefile.dat
以下字符串也可以代表同一个文件。
somefile.dat c:/temp/subdir/../somefile.dat c:/ temp/ somefile.dat ../somefile.dat c%3A%5Ctemp%5Csubdir%5C%2E%2E%5Csomefile.dat
最后一个示例使用十六进制指定字符:
• |
%3A 代表冒号。 |
• |
%5C 代表反斜杠符号。 |
• |
%2E 代表句号。 |
通常,应设法避免让应用程序接受用户输入的文件名,以防止标准化问题。可以考虑其他设计方法。例如,让应用程序为用户确定文件名。
如果确实需要用户输入文件名,在作出安全决策(如授予或拒绝对特定文件的访问权限)之前,应确保这些文件名具有严格定义的形式。
有关如何处理文件名以及如何以安全方式执行文件 I/O 的详细信息,请参阅模块 7 中的“文件 I/O”部分:构建安全的程序集,以及模块 8:代码访问安全的实践。
限制、拒绝和净化输入
输入验证的首选方法是从开始就限制允许输入的内容。按照已知的有效类型、模式和范围验证数据要比通过查找已知有害字符的数据验证方法容易。设计应用程序时,应了解应用程序需要输入什么内容。与潜在的恶意输入相比,有效数据的范围通常是更为有限的集合。然而,为了使防御更为彻底,您可能还希望拒绝已知的有害输入,然后净化输入。图 4.4 显示了我们推荐的策略。
图 4.4
输入验证策略:限制、拒绝和净化输入
要创建有效的输入验证策略,需了解以下方法及其折衷方案:
• |
限制输入。 |
• |
按照类型、长度、格式和范围验证数据。 |
• |
拒绝已知的有害输入。 |
• |
净化输入。 |
限制输入
限制输入与允许输入有益数据类似。这是首选的输入验证方法。其思想是定义一个筛选器,根据类型、长度、格式和范围来筛选输入的数据。定义应用程序字段可以接受的数据输入,并强制应用该定义。拒绝一切有害数据。
限制输入可能包括在服务器上设置字符集,以便在本地建立输入的规范格式。
验证数据的类型、长度、格式和范围
在适当的地方对输入数据使用强类型检查,例如,在用于操作和处理输入数据的类中,以及在数据访问例程中。例如,可以使用参数化的存储过程来访问数据,以便利用输入字段的强类型检查所带来的好处。
应该检查字符串字段的长度,在许多情况下,还应检查字符串的格式是否正确。例如,邮政编码和身份证号码等都具有明确定义的格式,可以使用常规表达式进行验证。严格的检查不仅是很好的编程习惯,还能让攻击者更难利用您的代码。攻击者可能会通过类型检查,但长度检查会加大攻击者实施其所喜欢的攻击方式的难度。
拒绝已知的有害输入
虽然不能完全依赖于这种方法,但还是应该拒绝“有害”数据。此方法通常不会像使用上述的“允许”方法那样有效,但二者结合使用可以收到最佳效果。要拒绝有害数据,需假定应用程序知道恶意输入的所有变体。请记住,字符有多种表达方式。这是“允许”方法成为首选方法的另一个原因。
虽然在应用程序已经部署、不能再做重大更改时,“拒绝”方法非常有用,但它不如“允许”方法那样可靠,因为有害数据(如可用于识别常见攻击的样式)不是保持不变的。有效数据的形式是保持不变的,但有害数据的范围却是时常变化的。
净化输入
净化是为了使有潜在危害的数据变得安全。如果所允许的输入范围不能保证输入数据的安全性,那么净化输入是非常有用的。净化包括从删除用户输入字符串后面的空格到去除值(以便按照文字格式处理该数据)等一切行为。
在 Web 应用程序中,另一个常见的净化输入示例是使用 URL 编码或 HTML 编码来包装数据,并将其作为文本而不是可执行脚本来处理。HtmlEncode 方法去除 HTML 字符,而 UrlEncode 方法对 URL 进行编码,使其成为有效的 URI 请求。
在实践中
以下是使用上述方法处理常见输入字段的几个示例:
• |
姓氏字段。这是一个很好的应用限制输入的示例。在这种情况下,可以接受的字符串范围为 ASCII A–Z 和 a–z,以及连字符和波浪线(在 SQL 中,波浪线没有意义),以便处理类似 O'Dell 之类的姓氏。还应限制输入内容的最大长度。 |
||||
• |
数量字段。这是应用输入限制的另一个例子。在此示例中,可以使用简单的类型和范围限制。例如,输入数据应该是介于 0 和 1000 之间的正整数。 |
||||
• |
自定义文本字段。示例包括留言版上的备注字段。在这种情况下,您可能允许输入字母和空格,以及省略符号、逗号和连字符等常用字符。允许输入的字符集只包括符号、括号和大括号。 有些应用程序可能允许用户使用一组有限的脚本字符修饰文本,如粗体“<b>”、斜体“<b>”,甚至包含指向他们所喜爱的 URL 的连接。处理 URL 时,验证时应先对所输入的值进行编码,以便将其作为 URL 处理。 有关验证自定义文本字段的详细信息,请参阅模块 10 中的“输入验证”部分:构建安全的 ASP.NET 网页和控件。 |
||||
• |
不验证用户输入的现有 Web 应用程序。在理想方案中,应用程序将检查每个字段和入口点的输入内容是否可以接受。然而,如果现有 Web 应用程序不验证用户输入,则需要一种权宜方法来降低风险,直到改进应用程序的输入验证策略。以下两种方法都不能确保输入数据的安全处理,因为这要依赖于输入的来源,以及应用程序使用输入数据的方式,目前,它们作为快速的补救措施,能在短期内提高应用程序的安全性:
|
有关输入编码、使用常规表达式以及 ASP.NET 验证控件的详细信息和示例,请参阅模块 10 中的“输入验证”:构建安全的 ASP.NET 页面和控件。
身份验证
身份验证是确定调用方身份的过程。有三方面问题需要考虑:
• |
确定应用程序中何处需要身份验证。通常在跨越信任边界时,都需要进行身份验证。信任边界通常包括程序集、过程和主机。 |
• |
确认调用方的身份。用户通常使用用户名和密码证明自己的身份。 |
• |
在后续请求中识别用户。这需要某种形式的验证令牌。 |
许多 Web 应用程序使用密码机制对用户进行身份验证,用户可以在 HTML 表单中输入用户名和密码。这里需要考虑的问题包括:
• |
用户名和密码是否通过不安全的通道以纯文本形式发送?如果答案是肯定的,攻击者可以通过网络监视软件进行窃听,以捕获凭据。对付此类攻击的对策是使用安全套接字层 (SSL) 确保通信通道的安全。 |
• |
如何存储凭据?如果在文件或数据库中以纯文本形式存储用户名和密码,则会惹来麻烦。设想如果应用程序目录配置不当,使攻击者浏览到该文件并下载了其内容,或者添加了新的特权登录帐户,后果如何?如果怀有敌意的管理员得到了您的用户名和密码数据库,后果如何? |
• |
如何验证凭据?如果唯一目的是验证用户是否知道密码值,则没有必要存储用户密码。可以使用哈希值的形式存储验证程序,当用户登录时,根据用户所输入的值重新计算哈希值。为减少对凭据存储的词典攻击威胁,应使用强密码,并将随机生成的 salt 值与密码哈希结合使用。 |
• |
初次登录后,怎样识别已通过身份验证的用户?这需要某种形式的验证票证,如身份验证 cookie。怎样保证 cookie 的安全?如果通过不安全的通道来发送 cookie,攻击者可以捕获该 cookie 值,并使用它来访问应用程序。身份验证 cookie 被窃取意味着登录被窃取。 |
以下做法可以增强 Web 应用程序的身份验证:
• |
区分公共区域和受限区域。 |
• |
对最终用户帐户使用帐户锁定策略。 |
• |
支持密码有效期。 |
• |
能够禁用帐户。 |
• |
不要在用户存储中存储密码。 |
• |
要求使用强密码。 |
• |
不要在网络上以纯文本形式发送密码。 |
• |
保护身份验证 cookie。 |
区分公共区域和受限区域
站点的公共区域允许任何用户进行匿名访问。受限区域只能接受特定用户的访问,而且用户必须通过站点的身份验证。考虑一个典型的零售网站。您可以匿名浏览产品分类。当您向购物车中添加物品时,应用程序将使用会话标识符验证您的身份。最后,当您下订单时,即可执行安全的交易。这需要您进行登录,以便通过 SSL 验证交易。
将站点分割为公共访问区域和受限访问区域,可以在该站点的不同区域使用不同的身份验证和授权规则,从而限制对 SSL 的使用。使用 SSL 会导致性能下降,为了避免不必要的系统开销,在设计站点时,应该在要求验证访问的区域限制使用 SSL。
对最终用户帐户使用帐户锁定策略
当最终用户帐户几次登录尝试失败后,可以禁用该帐户或将事件写入日志。如果使用 Windows 验证(如 NTLM 或 Kerberos 协议),操作系统可以自动配置并应用这些策略。如果使用表单验证,则这些策略是应用程序应该完成的任务,必须在设计阶段将这些策略合并到应用程序中。
请注意,帐户锁定策略不能用于抵制服务攻击。例如,应该使用自定义帐户名替代已知的默认服务帐户(如 IUSR_MACHINENAME),以防止获得 Internet 信息服务 (IIS) Web 服务器名称的攻击者锁定这一重要帐户。
支持密码有效期
密码不应固定不变,而应作为常规密码维护的一部分,通过设置密码有效期对密码进行更改。在应用程序设计阶段,应该考虑提供这种类型的功能。
能够禁用帐户
如果在系统受到威胁时使凭证失效或禁用帐户,则可以避免遭受进一步的攻击。
不要在用户存储中存储密码
如果必须验证密码,则没有必要实际存储密码。相反,可以存储一个单向哈希值,然后使用用户所提供的密码重新计算哈希值。为减少对用户存储的词典攻击威胁,可以使用强密码,并将随机 salt 值与该密码结合使用。
要求使用强密码
不要使攻击者能轻松破解密码。有很多可用的密码编制指南,但通常的做法是要求输入至少 8 位字符,其中要包含大写字母、小写字母、数字和特殊字符。无论是使用平台实施密码验证还是开发自己的验证策略,此步骤在对付粗暴攻击时都是必需的。在粗暴攻击中,攻击者试图通过系统的试错法来破解密码。使用常规表达式协助强密码验证。
有关使用常规表达式协助密码验证的示例,请参阅模块 10 中的“输入验证”部分:构建安全的 ASP.NET 网页和控件。
不要在网络上以纯文本形式发送密码
以纯文本形式在网络上发送的密码容易被窃听。为了解决这一问题,应确保通信通道的安全,例如,使用 SSL 对数据流加密。
保护身份验证 Cookie
身份验证 cookie 被窃取意味着登录被窃取。可以通过加密和安全的通信通道来保护验证票证。另外,还应限制验证票证的有效期,以防止因重复攻击导致的欺骗威胁。在重复攻击中,攻击者可以捕获 cookie,并使用它来非法访问您的站点。减少 cookie 超时时间虽然不能阻止重复攻击,但确实能限制攻击者利用窃取的 cookie 来访问站点的时间。
授权
授权确定已通过验证的标识可以执行哪些操作以及可以访问哪些资源。错误授权或弱授权会导致信息泄漏和数据篡改。深入防御是应用程序授权策略的关键安全原则。
以下做法可以增强 Web 应用程序的授权:
• |
使用多重看守。 |
• |
限制用户对系统级资源的访问。 |
• |
考虑授权粒度。 |
使用多重看守
在服务器端,可以使用 IP 安全协议 (IPSec) 策略提供主机限制,以此来限制服务器间的通信。例如,IPSec 策略可以限制远离指定 Web 服务器的任何主机连接到数据库服务器。IIS 提供了 Web 权限、Internet 协议/域名系统 (IP/DNS) 限制。无论用户是什么身份,IIS 的 Web 权限适用于所有通过 HTTP 请求的资源。如果攻击者设法登录到服务器,IIS 的 Web 权限将不提供保护功能。因此,NTFS 权限允许您为每个用户指定访问控制列表。最后,ASP.NET 提供 URL 授权和文件授权以及主要权限需求。将这些看守方法结合使用,可以制定出有效的授权策略。
限制用户对系统级资源的访问
系统级资源包括文件、文件夹、注册表项、Active Directory 对象、数据库对象、事件日志,等等。使用 Windows 访问控制列表 (ACL) 限制哪些用户可以访问哪些资源,以及可以执行哪些类型的操作。要特别注意匿名 Interne 用户帐户;使用 ACL 锁定这些匿名帐户,以防止他们访问明确拒绝匿名用户访问的资源。
有关使用 Windows ACL 锁定匿名 Internet 用户帐户的详细信息,请参阅模块 16:保护 Web 服务器。
考虑授权粒度
有三个常用的授权模型,每个模型都具有不同的粒度和可伸缩性。
最多粒度法建立在模拟的基础上。资源访问是使用调用方的安全上下文实现的。安全资源(通常指文件或表格,或二者都包括)上的 Windows ACL 确定是否允许调用方访问该资源。如果应用程序主要提供对用户特定资源的访问,那么这种方法可能是有效的。它还具有另一个优点,即能够跨应用层执行操作系统级审核,因为原始调用方的安全上下文在操作系统级传送,并用于资源访问的依据。然而,如果应用程序的可伸缩性较差,这种方法的效果会受影响,因为不存在高效的数据库访问连接池。因此,此方法通常应用于规模有限的基于 Intranet 的应用程序中。图 4.5 显示了模拟模型。
图 4.5
模拟模型提供了每位最终用户的授权粒度
最少粒度最大伸缩性方法使用应用程序的过程标识来访问资源。此方法支持数据库连接池,但这说明授予数据库中应用程序标识的权限是公用的,而不考虑原始调用方的标识。初级授权是在应用程序的逻辑中间层通过角色实现的,角色将在应用程序中共享相同权限的用户分为一组。基于调用方的角色成员身份来限制其对类和方法的访问权限。为了支持对用户个人数据的检索,常用方法是在数据库表中添加一个标识列,并使用查询参数限制检索到的数据。例如,可以在应用程序级(而不是操作系统级)使用存储的过程参数将原始调用方的标识传递到数据库中,然后编写类似如下所示的查询语句:
SELECT field1,field2,field3 FROM Table1 WHERE {some search criteria} AND UserName = @originalCallerUserName
此模型被认为是受信任的子系统,有时还被称作受信任的服务器模型。图 4.6 列出了该模型。
图 4.6
支持数据库连接池的受信任子系统模型
第三种方法基于调用方的角色成员身份,使用有限的标识集进行资源访问。实际上它是前面所述的两种模型的综合。调用方被映射到应用程序逻辑中间层中的角色,并基于角色成员身份限制对类和方法的访问权限。使用由当前调用方的角色成员身份所确定的有限标识集来执行下游资源访问。该方法的优势在于可以在每次登录数据库时单独分配权限,而多重连接池仍然有效。多线程访问令牌使用 Windows 验证为下游资源访问建立不同的安全上下文。此方法的劣势在于创建这种令牌是一种特权操作,需要使用特权进程帐户。这是与最少特权原则相违背的。混合模型使用多重受信任服务标识管理对下游资源的访问权限,图 4.7 中显示了这种模型。
图 4.7
混合模型
配置管理
应该仔细考虑 Web 应用程序的配置管理功能。大多数应用程序需要使用接口,通过接口,内容开发人员、操作员和管理员可以配置应用程序和管理事项,如 Web 页内容、用户帐户、用户配置文件信息和数据库连接字符串。如果支持远程管理,如何确保管理界面的安全?如果管理界面存在安全漏洞,结果会很严重,因为攻击者常常利用管理员特权中止程序运行,并能直接访问整个站点。
以下做法可以提高 Web 应用程序配置管理的安全性:
• |
确保管理界面的安全。 |
• |
确保配置存储的安全。 |
• |
维护单独的管理特权。 |
• |
使用最少特权进程和服务帐户。 |
确保管理界面的安全
配置管理功能只能由经过授权的操作员和管理员访问,这一点是非常重要的。关键一点是要在管理界面上实施强身份验证,如使用证书。
如果有可能,限制或避免使用远程管理,并要求管理员在本地登录。如果需要支持远程管理,应使用加密通道,如 SSL 或 VPN 技术,因为通过管理界面传递的数据是敏感数据。此外,还要考虑使用 IPSec 策略限制对内部网络计算机的远程管理,以进一步降低风险。
确保配置存储的安全
基于文本的配置文件、注册表和数据库是存储应用程序配置数据的常用方法。如有可能,应避免在应用程序的 Web 空间使用配置文件,以防止可能出现的服务器配置漏洞导致配置文件被下载。无论使用哪种方法,都应确保配置存储访问的安全,如使用 Windows ACL 或数据库权限。还应避免以纯文本形式存储机密,如数据库连接字符串或帐户凭据。通过加密确保这些项目的安全,然后限制对包含加密数据的注册表项、文件或表的访问权限。
单独分配管理特权
如果应用程序的配置管理功能所支持的功能性基于管理员角色而变化,则应考虑使用基于角色的授权策略分别为每个角色授权。例如,负责更新站点静态内容的人员不必具有更改客户信贷限额的权限。
使用最少特权进程和服务帐户
应用程序配置的一个重要方面是用于运行 Web 服务器进程的进程帐户,以及用于访问下游资源和系统的服务帐户。应确保为这些帐户设置最少特权。如果攻击者设法控制一个进程,则该进程标识对文件系统和其他系统资源应该具有极有限的访问权限,以减少可能造成的危害。
敏感数据
处理诸如信用卡号、地址、病例档案等用户私人信息的应用程序应该采取专门的步骤,来确保这些数据的保密性,并确保其不被修改。另外,实现应用程序时所用的机密数据(如数据库连接字符串)必须是安全的。在永久性存储中存储敏感数据以及在网络上传递敏感数据时,数据的安全性是一个需要解决的问题。
机密
机密包括密码、数据库连接字符串和信用卡号。以下做法可以提高 Web 应用程序机密数据处理的安全性:
• |
尽量避免存储机密。 |
• |
不要在代码中存储机密。 |
• |
不要以纯文本形式存储数据库连接、密码或密钥。 |
• |
避免在本地安全性机构 (LSA) 中存储机密。 |
• |
使用数据保护 API (DPAPI) 对机密数据加密。 |
尽量避免存储机密
在软件中以完全安全的方式存储机密是不可能的。可以接触到服务器的系统管理员可以访问这些数据。例如,当您所要做的仅仅是验证用户是否知道某个机密时,则没有必要存储该机密。在这种情况下,可以存储代表机密的哈希值,然后使用用户提供的值计算哈希值,以验证该用户是否知道该机密。
不要在代码中存储机密
不要在代码中对机密进行硬编码。即使不将源代码暴露在 Web 服务器上,但从编译过的可执行文件中仍然可以提取字符串常量。配置漏洞可能会允许攻击者检索可执行文件。
不要以纯文本形式存储数据库连接、密码或密钥
避免以纯文本形式存储诸如数据库连接字符串、密码和密钥之类的机密。使用加密,并存储经过加密的字符串。
避免在 LSA 中存储机密
避免使用 LSA,因为只有具有管理特权的应用程序才能访问它。而这一点违反了以最少特权运行的核心安全原则。另外,LSA 只能在有限数量的插槽中存储机密。更好的方法是使用 DPAPI,Microsoft Windows® 2000及其以后的操作系统都提供了该功能。
使用 DPAPI 对机密数据加密
要存储诸如数据库连接字符串或服务帐户凭据之类的机密,应使用 DPAPI。使用 DPAPI 的主要优势在于由平台系统管理加密/解密密钥,而不是由应用程序负责。密钥依附于 Windows 用户帐户或特定计算机,这取决于传递到 DPAPI 函数的标志。
当主密钥丢失时(例如,因为服务器被损坏而需要重新安装操作系统),DPAPI 最适合于为可手动重建的信息加密。 对于有些数据(如客户信用卡详细信息),因为不知道其纯文本值而不能恢复,这时需要使用另外一种方法,即使用基于密钥的传统对称加密法,如使用 triple–DES。
有关从 Web 应用程序使用 DPAPI 的详细信息,请参阅模块10:构建安全的 ASP.NET 网页和控件。
敏感的用户个人数据
必须保护敏感数据,如登录凭据和应用程序级数据(如信用卡号、银行帐户号等)。通过加密获得的保密性和通过消息验证代码 (MAC) 获得的完整性是重要因素。
以下做法可以提高 Web 应用程序中敏感的用户个人数据的安全性:
• |
根据需要检索敏感数据。 |
• |
对数据进行加密或确保通信通道的安全。 |
• |
不要在永久性 cookie 中存储敏感数据。 |
• |
不要使用 HTTP-GET 协议传递敏感数据。 |
根据需要检索敏感数据
首选方法是根据需要检索敏感数据,而不是将其保留或高速缓存在内存中。例如,在需要已加密的机密时对其进行检索,然后在解密后使用该数据,最后清空用于保留纯文本机密的内存(变量)。如果考虑性能因素,请注意以下几个选项:
• |
缓存已加密的机密。 |
• |
缓存纯文本机密。 |
缓存已加密的机密
应用程序加载时检索机密,然后将已加密的机密缓存在内存中,当应用程序使用该机密时再将其解密。当程序不再需要该机密时,清除其纯文本副本。这种方法避免了每次请求都要访问数据存储。
缓存纯文本机密
避免因多次解密机密而造成的系统开销,并将机密的纯文本副本存储在内存中。这是最不安全的做法,却提供了最好的性能。在权衡提高了的性能和增大的安全风险之前,应对其他方法做基准检查。
对数据进行加密或确保通信通道的安全
如果在网络上向客户端发送敏感数据,应对数据进行加密或确保通信通道的安全。通常的做法是在客户端与 Web 服务器之间使用 SSL。服务器间的通信通常使用 IPSec。要确保通过多重中间件传输的敏感数据的安全性,如 Web 服务简单对象访问协议 (SOAP) 消息,应使用消息级加密。
不要在永久性 cookie 中存储敏感数据
避免在永久性 cookie 中存储敏感数据。如果存储的是纯文本数据,最终用户能够看到并修改该数据。如果对其加密,必须考虑密钥管理。例如,如果用于加密 cookie 中的数据的密钥已过期且已被回收,则新密钥不能对客户端通过浏览器传递的永久性 cookie 进行解密。
不要使用 HTTP-GET 协议传递敏感数据
应避免使用 HTTP-GET 协议存储敏感数据,因为该协议使用查询字符串传递数据。使用查询字符串不能确保敏感数据的安全性,因为查询字符串经常被服务器记录下来。
会话管理
Web 应用程序基于无界限的 HTTP 协议构建,因此,会话管理是应用程序级职责。对于应用程序总体安全来讲,会话安全是关键因素。
以下做法可以提高 Web 应用程序会话管理的安全性:
• |
使用 SSL 保护会话身份验证 cookie。 |
• |
对身份验证 cookie 的内容进行加密。 |
• |
限制会话寿命。 |
• |
避免未经授权访问会话状态。 |
使用 SSL 保护会话身份验证 Cookie
不要通过 HTTP 连接传递身份验证 cookie。在授权 cookie 内设置安全的 cookie 属性,以便指示浏览器只通过 HTTPS 连接向服务器传回 cookie。有关详细信息,请参阅模块 10:构建安全的 ASP.NET 网页和控件。
对身份验证 cookie 的内容进行加密
即使使用 SSL,也要对 cookie 内容进行加密。如果攻击者试图利用 XSS 攻击窃取 cookie,这种方法可以防止攻击者查看和修改该 cookie。在这种情况下,攻击者仍然可以使用 cookie 访问应用程序,但只有当 cookie 有效时,才能访问成功。
限制会话寿命
缩短会话寿命可以降低会话劫持和重复攻击的风险。会话寿命越短,攻击者捕获会话 cookie 并利用它访问应用程序的时间越有限。
避免未经授权访问会话状态
考虑会话状态的存储方式。为获得最佳性能,可以将会话状态存储在 Web 应用程序的进程地址空间。然而这种方法在 Web 场方案中的可伸缩性和内涵都很有限,来自同一用户的请求不能保证由同一台服务器处理。在这种情况下,需要在专用状态服务器上进行进程外状态存储,或者在共享数据库中进行永久性状态存储。ASP.NET 支持所有这三种存储方式。
对于从 Web 应用程序到状态存储之间的网络连接,应使用 IPSec 或 SSL 确保其安全,以降低被窃听的危险。另外,还需考虑 Web 应用程序如何通过状态存储的身份验证。在可能的地方使用 Windows 验证,以避免通过网络传递纯文本身份验证凭据,并可利用安全的 Windows 帐户策略带来的好处。
加密
基本加密方式提供:
• |
保密性 (机密性)。此服务保持数据的机密性。 |
• |
认可(真实性)。此服务确保用户不能拒绝发送特定消息。 |
• |
防止篡改(完整性)。此服务防止数据被修改。 |
• |
身份验证。此服务确认消息发送方的身份。 |
Web 应用程序通常使用加密方式来确保永久性存储中的数据或在网络上传输的数据的安全性。在使用加密方法时,下列做法可以提高 Web 应用程序的安全性:
• |
不要自创加密方法。 |
• |
将未加密数据存储在算法附近。 |
• |
使用正确的算法和密钥大小。 |
• |
确保加密密钥的安全。 |
不要自创加密方法
成功开发出加密算法和例程是非常难的。因此,应使用平台提供的经过验证和测试的加密服务。这包括 .NET 框架和基础操作系统。不要开发自定义的实现方法,因为这通常会导致保护能力减弱。
将未加密数据存储在算法附近
向算法传递纯文本时,除非需要使用,否则不要获取该数据,并要以尽可能少的修改存储该数据。
使用正确的算法和密钥大小
是否为一项作业选择了正确的算法非常重要,另外,应确保所使用的密钥大小能提供足够的安全级别。密钥越大,安全性越高。以下列表概括了主要算法及其使用的密钥大小:
• |
数据加密标准 (DES) 64 位密钥(8 个字节) |
• |
TripleDES 128 位密钥或 192 位密钥(16 或 24 个字节) |
• |
Rijndael 128 – 256 位密钥(16 – 32 个字节) |
• |
RSA 384 – 16,384 位密钥(48 – 2,048 个字节) |
对于大量数据加密,应使用 TripleDES 对称加密算法。要减慢并加强对大量数据的加密,应使用 Rijndael 算法。要对将暂时存储的数据加密,可以考虑使用较快但较弱的算法,如 DES。对于数字签名,应使用 RSA 或 DSA 算法。对于哈希,应使用 SHA1.0 算法。对于用户键入的哈希,应使用基于哈希的消息验证代码(HMAC)SHA1.0 算法。
确保加密密钥的安全
加密密钥是输入加密及解密进程的秘密数字。为保证加密数据的安全,必须保护好密钥。一旦攻击者得到了解密密钥,加密数据就不再安全了。
以下做法将有助于确保加密密钥的安全:
• |
使用 DPAPI 来回避密钥管理。 |
• |
定期回收密钥。 |
使用 DPAPI 来回避密钥管理
如前所述,使用 DPAPI 的主要优势之一在于密钥管理问题是由操作系统处理的。DPAPI 使用的密钥是从密码中导出的,该密码与调用 DPAPI 函数的进程帐户相关联。使用 DPAPI 将密钥管理的任务交给操作系统。
定期回收密钥
通常,保持不变的机密数据很容易随着时间的推移而被发现。必须牢记以下问题:是否在什么地方写下了密钥?了解这些机密的管理员是否已更换职位或离职?不要过度使用密钥。
参数操作
利用参数操作攻击,攻击者能够修改在客户端与 Web 应用程序间发送的数据。此数据可能是使用查询字符串、表单字段、cookie 或 HTTP 头发送的。以下做法有助于确保 Web 应用程序参数操作的安全:
• |
加密敏感的 cookie 状态。 |
• |
确保用户没有绕过检查。 |
• |
验证从客户端发送的所有值。 |
• |
不要信任 HTTP 头信息。 |
机密敏感的 cookie 状态
Cookie 中可能包含敏感数据,如会话标识符或用作服务器端授权进程一部分的数据。要保护该类型数据,应使用加密方法对 cookie 的内容加密。
确保用户没有绕过检查
确保用户没有通过操作参数而绕过检查。最终用户可以通过浏览器地址文本框操作 URL 参数。例如,URL 地址 http://www.<YourSite>/<YourApp>/sessionId=10 包含一个值 10,通过将该值更改为其他随机数字,可以得到不同的输出。应确保在服务器端代码中执行上述检查,而不是在客户端的 JavaScript 中检查,因为可以在浏览器中禁用 JavaScript。
验证从客户端发送的所有数据
限制可接受用户输入的字段,并对来自客户端的所有值进行修改和验证。如果表单字段中包含预定义值,用户可以更改这些值,并将其传回服务器,以得到不同的结果。只接受已知的有益数据。例如,如果输入字段面向一个州,那么只有与该州邮政编码匹配的输入才能被接受。
不要信任 HTTP 头信息
HTTP 头在 HTTP 请求和响应开始时发送。应确保 Web 应用程序的任何安全决策都不是基于 HTTP 头中包含的信息,因为攻击者很容易操作 HTTP 头。例如,HTTP 头中的“referer”字段包含发出请求的网页的 URL。不要基于“referer”字段值作出任何安全决策,以检查发出请求的页面是否由该 Web 应用程序生成,因为该字段很容易伪造。
异常管理
安全的异常处理有助于阻止某些应用程序级拒绝服务攻击,还可用来防止对攻击者有用的宝贵系统级信息被返回给客户端。例如,如果没有正确的异常处理机制,数据库架构详细信息、操作系统版本、堆栈跟踪、文件名和路径信息、SQL 查询字符串以及对攻击者有用的其他信息可以被返回给客户端。
一种好的方法是设计一个集中式异常管理和记录解决方案,并考虑在异常管理系统中提供挂钩,以支持规范和集中式监视,从而为系统管理员提供帮助。
以下做法有助于确保 Web 应用程序异常管理的安全:
• |
不要向客户端泄漏信息。 |
• |
记录详细的错误信息。 |
• |
捕捉异常。 |
不要向客户端泄漏信息
发生故障时,不要暴露将会导致信息泄漏的消息。例如,不要暴露包括函数名以及调试内部版本时出问题的行数(该操作不应在生产服务器上进行)的堆栈跟踪详细信息。应向客户端返回一般性错误消息。
记录详细的错误信息
向错误日志发送详细的错误消息。应该向服务或应用程序的客户发送最少量的信息,如一般性错误消息和自定义错误日志 ID,随后可以将这些信息映射到事件日志中的详细消息。确保没有记录密码或其他敏感数据。
捕捉异常
使用结构化异常处理机制,并捕捉异常现象。这样做可以避免将应用程序置于不协调的状态,这种状态可能会导致信息泄漏。它还有助于保护应用程序免受拒绝服务攻击。确定如何在应用程序内部广播异常现象,并着重考虑在应用程序的边界会发生什么事情。
有关设计和实现 .NET 应用程序异常管理框架的详细信息,请参阅 MSDN 文章“Exception Management Architecture Guide”,其网址为:http://msdn.microsoft.com/library/en-us/dnbda/html/exceptdotnet.asp(英文)。
审核和记录
应该审核和记录跨应用层的活动。使用日志,可以检测到踪迹可疑的活动。这通常能较早地发现成熟攻击的迹象,日志还有助于解决抵赖问题,即用户拒绝承认其行为的问题。在证明个人错误行为的法律程序中,可能需要使用日志文件作为证据。通常情况下,如果审核的生成时间恰好是资源访问的时间,并且使用相同的资源访问例程,则审核是最具权威性的。
以下做法可以提高 Web 应用程序的安全性:
• |
审核并记录跨应用层的访问。 |
• |
考虑标识流。 |
• |
记录关键事件。 |
• |
确保日志文件的安全。 |
• |
定期备份和分析日志文件。 |
审核并记录跨应用层的访问
审核并记录跨应用层的访问,以便用于认可。可以结合使用应用程序级记录和平台审核功能,如 Windows、IIS 和 SQL Server 审核。
考虑标识流
考虑应用程序如何在多重应用层间传送调用方标识。有两个基本选择。可以使用 Kerberos 协议委派,在操作系统级传送调用方标识。这允许您使用操作系统级审核。这种方法的缺点在于它影响了可伸缩性,因为它意味着在中间层可能没有有效的数据库连接池。另外,还可以在应用程序级传送调用方标识,并使用受信任标识访问后端资源。使用此方法时,必须信任中间层,因此存在着潜在的抵赖风险。应在中间层生成审核跟踪,使之能与后端审核跟踪相关联。因此,必须确保服务器时钟是同步的,虽然 Microsoft Windows 2000 和 Active Directory 提供了此项功能。
记录关键事件
应记录的事件类型包括成功和失败的登录尝试、数据修改、数据检索、网络通信和管理功能,如启用或禁用日志记录。日志应包括事件发生的时间、地点(包括主机名)、当前用户的标识、启动该事件的进程标识以及对该事件的详细描述。
确保日志文件的安全
应使用 Windows ACL 确保日志文件的安全,并限制对日志文件的访问。这加大了攻击者篡改日志文件以掩饰其攻击行为的难度。应将有权操作日志文件的个人数量减到最小。只为高度信任的帐户(如管理员)授予访问权限。
定期备份和分析日志文件
如果从不对日志文件进行分析,则记录活动没有任何意义。应定期删除生产服务器上的日志文件。删除频率取决于应用程序的活动级别。设计程序时,应考虑检索日志文件和将其移到脱机服务器进行分析的方式。必须安全地锁定 Web 服务器上为此目的打开的所有其他协议和端口。
设计指南小结
表 4.1 总结了本模块中讨论的设计指南,并按照应用程序的漏洞类别对其进行了组织。
表 4.1.应用程序设计指南
类别 | 指南 |
输入验证 |
不要信任输入;应考虑集中式输入验证。 |
身份验证 |
将站点分割为匿名区域、标识区域和通过身份验证的区域。使用强密码。支持密码有效期和帐户禁用。不要存储凭据(应使用带有 salt 的单向哈希)。加密通信通道,以保护身份验证令牌。仅通过 HTTPS 连接传递表单身份验证 cookie。 |
授权 |
使用最少特权帐户。考虑授权粒度。实施分别授权。限制用户访问系统级资源。 |
配置管理 |
使用最少特权进程和服务帐户。不要以纯文本形式存储凭据。在管理界面上使用强身份验证和授权。不要使用 LSA。远程管理时要确保通信通道的安全。避免在 Web 空间中存储敏感数据。 |
敏感数据 |
避免存储机密。对网络上传输的敏感数据进行加密。确保通信通道的安全。对敏感数据存储提供强访问控制。不要在永久性 cookie 中存储敏感数据。不要使用 HTTP-GET 协议传递敏感数据。 |
会话管理 |
限制会话寿命。确保通道的安全。对身份验证 cookie 的内容进行加密。保护会话状态,以防止未经授权的访问。 |
加密 |
不要自创加密算法。使用可靠并经过测试的平台功能。将未加密的数据存储在算法附近。使用正确的算法和密钥大小。避免密钥管理(使用 DPAPI)。定期回收密钥。在受限区域存储密钥。 |
参数操作 |
对敏感的 cookie 状态加密。不要信任客户端可以操作的字段(如查询字符串、表单字段、cookie 或 HTTP 头)。验证从客户端发送的所有数据。 |
异常管理 |
使用结构化的异常处理机制。不要泄漏敏感的应用程序实施细节。不要记录保密数据,如密码。考虑使用集中式的异常管理框架。 |
审核和记录 |
识别怀有恶意的行为。了解好的数据流应该是什么样子。在所有应用层中审核和记录活动。确保日志文件访问的安全。定期备份和分析日志文件。 |
总结
安全性应渗透在产品开发生命周期的所有阶段,还应成为应用程序设计的关键问题。应特别注意可靠的身份验证和授权策略的设计。还应记住的是,大多数应用程序级攻击都源于恶意形式的输入数据和薄弱的应用程序输入验证设计。本模块中提供的指南应该可以帮助您解决在设计和构建安全应用程序中遇到的各种挑战。
其他资源
有关详细信息,请参阅以下资源:
• |
本指南是用于帮助客户提高 Web 应用程序安全性的系列指南中的第二卷。有关对身份验证、授权以及跨分布式 Web 应用层安全通信进行体系结构设计、设计、构建和配置的详细信息,请参阅“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications:Authentication, Authorization, and Secure Communication”,其网址为:http://msdn.microsoft.com/library/en-us/dnnetsec/html/secnetlpMSDN.asp(英文)。 |
• |
MSDN上“Security Models for ASP.NET Applications”一文:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/SecNetch02.asp?frame=true(英文)。 |
• |
MSDN上“Designing Authentication and Authorization”一文:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/SecNetch03.asp?frame=true(英文)。 |
• |
本指南“检查表”部分中的检查表:体系结构和设计审查。 |