再探 Web 应用程序配置安全
Bryan Sullivan
几年前,在我来到 Microsoft 和安全性开发生命周期 (SDL) 团队之前,我写过一篇文章,阐述了不安全的 web.config 设置带来的危险,并指出了 10 大危险源。现在您仍可以找到这篇文章,只需在您喜欢的搜索引擎中搜索“Top 10 Application Security Vulnerabilities in Web.Config Files”即可。我以前提过的配置漏洞现在仍然存在,并且十分严重,虽然MSDN 杂志 的忠实读者对这些漏洞可能并不十分陌生。启用自定义错误,在应用程序投入生产环境之前禁用跟踪和调试以及对身份验证 Cookie 要求使用 SSL,这些仍然很重要。
在本月的专栏中,我希望补充上一篇文章未涉及的内容,并讨论某些更具隐蔽性但同样严重的安全配置错误问题。我还要介绍 Microsoft 信息安全工具团队研发的一款免费新工具,该工具称为“Web Application Configuration Analyzer”,可以帮您解决上述问题。请记住,即使是编码最安全的 ASP.NET 应用程序,在配置不正确的情况下也会被击溃。
EnableEventValidation
开发人员常犯的一个错误是为用户提供一个选择列表,然后假定用户实际会选择其中一个值。这看起来似乎合理:如果您在页面中添加一个 ListBox 控件,然后使用美国所有州的列表进行预填充,您预期返回的是“华盛顿”、“佐治亚”或“得克萨斯”,而不会想到返回“Foo”、“!@#$%”或“<script>alert(document.cookie);</script>”。在通过浏览器以传统方式使用应用程序时,可能无法像以上示例那样指定值,但却有多种方式,不用浏览器就可以访问 Web 应用程序!使用 Web 代理工具,如 Eric Lawrence 的 Fiddler(这一直是我最喜欢的用来查找 Web 应用程序中安全漏洞的工具之一,可以在如下网址下载:fiddler2.com),您可以随意向任何窗体字段发送任何值。如果您的应用程序没有为这种可能性做准备,可能就疏忽了一些有潜在危险的方式。
EnableEventValidation 配置设置是一种深层防御机制,有助于防范此种性质的攻击。如果恶意用户试图向接受确定值列表的控件(如 ListBox。TextBox 控件不在此列,因为它的性质已经可以接受任何值)发送意外值,则应用程序将检测到攻击并引发异常。
不好的代码:
<configuration>
<system.web>
<pages enableEventValidation="false"/>
好的代码:
<configuration>
<system.web>
<pages enableEventValidation="true"/>
PasswordFormat
ASP.NET(从 ASP.NET 2.0 开始)中包含的成员身份提供程序框架是一个很棒的功能,使开发人员无需一次又一次地重新创建成员身份功能。 通常,保留内置提供程序的默认设置是最安全的。 如果更改了成员身份配置设置,安全性会大大降低。
PasswordFormat 设置就是其中一个很好的示例,此设置确定如何存储用户密码。 您可以有三种选择:直接存储,即以纯文本方式存储密码;加密存储,即在存储密码前对其进行加密;哈希存储,即存储密码的哈希值而不是存储密码本身。 这几个选项中,直接存储是最不理想的方式。 永远不要以纯文本格式存储密码。 加密存储会好得多,哈希存储是最佳方式,因为存储秘密的最佳方式就是根本不存储。 然而,由于没有办法从哈希值检索原始密码,所以在用户忘记自己的密码时,您将不能为其恢复密码。
不好的代码:
<configuration>
<system.web>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
passwordFormat="Clear"
...
/>
较好的代码:
<configuration>
<system.web>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
passwordFormat="Encrypted"
...
/>
最好的代码:
<configuration>
<system.web>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
passwordFormat="Hashed"
...
/>
MinRequiredPasswordLength 和 MinRequiredNonalphanumericCharacters
成员身份设置中有两个值需要改变默认状态,即 MinRequiredPasswordLength 和 MinRequiredNonalphanumericCharacters。 对于 AspNetSqlMembershipProvider 对象,这些设置默认为至少需要使用长度为 6 个字符的密码,且不需要非字母数字字符。 为提高安全性,这些设置要求需要大大提高。 您应该要求使用至少 10 个字符长的密码,并至少包含两个非字母数字字符。 更好的方式是最少设置 14 个字符,并至少包含 4 个非字母数字字符。
密码长度和复杂性确实是一把双刃剑:您要求用户设置的密码越长越复杂,这些密码抵御穷举攻击的能力越强,但同时用户记不住密码且不得不写下密码的情况越可能发生。 虽然写下密码看似是一个可怕的潜在安全漏洞,但很多安全专家认为这样做利大于弊。 著名的安全大师 Bruce Schneier 就是其中之一,他建议用户创建长而复杂的密码,将其保存在钱包里,因为人们用钱包保存小纸片较安全。
不好的代码:
<configuration>
<system.web>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
minRequiredPasswordLength="6"
minRequiredNonalphanumericCharacters="0"
...
/>
好的代码:
<configuration>
<system.web>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
minRequiredPasswordLength="14"
minRequiredNonalphanumericCharacters="4"
...
/>
Microsoft 联机安全站点 (microsoft.com/protect/fraud/passwords/create.aspx) 也建议用户写下自己的密码,并提供了如何创建和确保强密码安全的附加信息。
ValidateRequest
跨站点脚本 (XSS) 仍是最常见的 Web 漏洞。 Cenzic Inc. 7 月份发布的一份报告 表明,上半年内,XSS 漏洞在所有 Web 攻击中占 28%。 由于 XSS 漏洞可能导致严重后果(我过去经常将 XSS 称为“Web 缓冲区溢出”),开发人员应竭尽所能防止其应用程序遭受此种攻击。 防范措施最好不花分文,ValidateRequest 能够做到这一点。
不好的代码:
<configuration>
<system.web>
<pages validateRequest="false" />
好的代码:
<configuration>
<system.web>
<pages validateRequest="true" />
ValidateRequest 工作时将测试用户输入中是否存在常见的攻击模式,如输入字符串是否包含尖括号 (<)。 如果存在这样的模式,则应用程序将引发异常,并停止处理请求。 虽然这本身不能构成一个完整的解决方案,您还需要一直应用输出编码和输入验证/净化逻辑(就像内置在 Microsoft Web Protection Library 中的逻辑一样),但 ValidateRequest 确实能够阻止很多种常见的 XSS 攻击。 最好尽可能将 ValidateRequest 置于启用状态。
MaxRequestLength
允许用户对您的应用程序做出任意大的 HTTP 请求并不明智。 因为这样容易遭受拒绝服务 (DoS) 攻击,此时一个攻击者就可以用尽您的所有带宽、处理器周期或磁盘空间,导致您的应用程序无法服务于任何其他合法用户。
为防止此类情况发生,您可以将 MaxRequestLength 属性适度地设置为较小的值。 默认值是 4096KB (4MB)。 对于正常和异常的请求大小,不同的应用程序有不同的要求,所以很难制定一个通用的规则规定设置怎样的 MaxRequestLength 值。 因此,这次不再举例说明“不好”和“好”的设置,只是建议您记住:此值设置越高,您遭受 DoS 攻击的风险越大:
<configuration>
<system.web>
<httpRuntime maxRequestLength="4096"/>
EnableViewStateMac
我在 2010 年 7 月的“安全简报”专栏发布过关于针对视图状态安全进行 EnableViewStateMac 设置的文章 (msdn.microsoft.com/magazine/ff797918)。 提醒一下没有阅读过此文的读者,EnableViewStateMac 可以防止攻击者篡改客户端视图状态。 启用 EnableViewStateMac 时,ASP.NET 应用程序会将加密消息验证代码 (MAC) 添加到 hidden __VIEWSTATE 表单值中。 攻击者无法确定有效的 MAC 以发起任意攻击(比如试图破坏攻击目标的视图状态以注入某些恶意 JavaScript),因此,如果攻击者试图以这种方式篡改视图状态,MAC 将无效且 ASP.NET 应用程序将阻止该请求。
不好的代码:
<configuration>
<system.web>
<pages enableViewStateMac="false"/>
好的代码:
<configuration>
<system.web>
<pages enableViewStateMac="true"/>
如果您在服务器场环境中部署应用程序,请务必为 MAC 手动指定密钥,而不是令应用程序自动生成随机密钥。 (如果您没有手动指定密钥,则场中的每个计算机将自动生成不同的密钥,任何计算机创建的视图状态 MAC 将被其他计算机视为无效并受到其他计算机阻止。)
手动创建密钥时要遵守一些额外的规则,以确保视图状态最佳安全性。 首先,确保指定一种 SDL 批准的加密算法。 对于使用 Microsoft .NET Framework 3.5 或更早版本的应用程序,这意味着使用 SHA1(默认算法)或 AES。 对于使用 .NET Framework 4 的应用程序,您还可以使用 HMACSHA256、HMACSHA384 或 HMACSHA512。 请避免使用 MD5 等较弱的算法。
不好的代码:
<configuration>
<system.web>
<machineKey validation="MD5" validationKey="..."/>
好的代码:
<configuration>
<system.web>
<machineKey validation="AES" validationKey="..."/>
选择强密钥和选择强算法一样重要。 可使用强加密随机数字生成器生成 64 字节密钥(使用 HMACSHA384 或 HMACSHA512 作为密钥算法时将生成 128 字节密钥)。 前面提到的 2010 年 7 月的“安全简报”专栏提供了生成正确密钥的参考示例代码。
不好的代码:
<configuration>
<system.web>
<machineKey validation="AES" validationKey="12345"/>
好的代码:
<configuration>
<system.web>
<machineKey validation="AES" validationKey="143a907bb73069a2fe7c..."/>
ViewStateEncryptionMode
您不但需要对应用程序视图状态应用 MAC 以便阻止潜在攻击者篡改视图状态,还需要对视图状态进行加密,以防止攻击者读取视图状态。 除非您完全确定您的任何视图状态中都没有敏感信息,否则最好设置 ViewStateEncryptionMode 属性以加密和保护视图状态。
不好的代码:
<configuration>
<system.web>
<pages viewStateEncryptionMode="Never"/>
好的代码:
<configuration>
<system.web>
<pages viewStateEncryptionMode="Auto"/>
像 EnableViewStateMac 一样,您可以为应用程序选择多种加密算法来加密视图状态。 但最好始终使用 AES,因为这是当前经过 SDL 加密标准批准的 唯一可用算法。
不好的代码:
<configuration>
<system.web>
<machineKey decryption="DES" decryptionKey=""/>
好的代码:
<configuration>
<system.web>
<machineKey decryption="AES" decryptionKey=""/>
最后,请记住,如果您在服务器场中部署应用程序,则需要手动指定密钥。 确保将密钥值设置为 24 字节加密随机值。
不好的代码:
<configuration>
<system.web>
<machineKey decryption="AES" decryptionKey="12345"/>
好的代码:
<configuration>
<system.web>
<machineKey decryption="AES" decryptionKey="143a907bb73069a2fe7c..."/>
UseUnsafeHeaderParsing
如果开发者饱受一个难以解决的 Bug 的困扰,他们往往实施任何自己学来的更改来解决问题,而不真正清楚自己对应用程序做了什么。 UseUnsafeHeaderParsing 设置是此种现象的经典示例。 虽然属性名中的“unsafe”一词足以警告多数开发人员,但在 Internet 简单搜索一下还是会出现成千上万建议开发者启用此属性的结果。 如果您启用了 UseUnsafeHeaderParsing,您的应用程序将忽略很多 HTTP RFC 规范,并试图解析非正常格式的请求。 虽然这样做使您的应用程序能够服务于不遵守 HTTP 标准的 HTTP 客户端(这也是很多人建议用此种办法解决问题的原因),但同时也可能使您的应用程序遭受格式不正确的标头的攻击。 为保持安全,请禁用此设置。
不好的代码:
<configuration>
<system.
net>
<settings>
<httpWebRequest
useUnsafeHeaderParsing=
"true"/>
好的代码:
<configuration>
<system.
net>
<settings>
<httpWebRequest
useUnsafeHeaderParsing=
"false"/>
Web Application Configuration Analyzer (WACA)
我们已经了解了一些危险的配置设置,现在看一下能帮助您自动查找代码中此类设置的工具。人工检查代码虽然可行,但自动分析毕竟可以更加彻底,更加一致。同时您也会从人工检查 XML 文件的枯燥工作中解放出来,腾出更多时间来解决更有意思的问题!
Microsoft 信息安全工具团队发布了一些优秀的安全工具。其中包括 AntiXSS/Web Protection Library 和 CAT.NET,我们已将这两个工具作为 Microsoft SDL 的一部分强制加入内部 .NET Framework Microsoft 产品和服务之中。在最新的版本中,WACA 设计为检测潜在的危险配置错误,如我在本文及以前的关于 10 大 常见 web.config 漏洞的文章中所述的错误。WACA 检查的一些示例:
- 是否启用了跟踪?
- MaxRequestLength 是否太大?
- 是否禁用了 HttpOnly Cookie?
- 进行窗体身份验证登录时是否需要 SSL?
- 是否已将 EnableViewStateMac 属性设置为 false?
另外,WACA 还可以检查 IIS 本身的配置错误,以及 SQL 数据库配置错误甚至系统级别问题。以下是一些具体示例:
- 是否已禁用 Windows 防火墙服务?
- 本地管理员是否名为“Administrator”?
- IIS 日志文件是否在系统驱动器上?
- 应用程序虚拟目录中是否启用了执行?
- SQL 服务器中是否存在示例数据库?
- SQL 服务器上是否启用了 xp_cmdshell?
虽然开发人员和测试人员多数情况下可能使用 WACA 检查应用程序配置设置,但系统管理员和数据库管理员会发现使用 WACA 检查 IIS、SQL 和系统设置十分有用(请参阅图 1)。WACA 中总共包含 140 多种根据 SDL 要求和模式以及实践代码指南做出的检查。
图 1 Web Application Configuration Analyzer 规则
WACA 还有一个非常方便的功能,就是可以在 Team Foundation Server (TFS) 团队项目中根据 WACA 扫描结果自动创建工作项或 Bug。针对从 SDL 过程模板或 MSF-Agile+SDL 过程模板创建的团队项目使用此功能时尤其有用。在 WACA TFS 设置页,将模板字段“Origin”映射到值“Web Application Configuration Analyzer”。然后在查看您的 Bug 报告或趋势图表时,您将能过滤和深入查看 WACA 结果,以查看此功能在检测潜在漏洞时的神奇效果 (请参阅图 2)。
图 2 WACA Team Foundation Server 集成
您可以在 Microsoft IT InfoSec 小组的页面 (msdn.microsoft.com/security/dd547422) 阅读有关 WACA 的详细资料,还可以观看 WACA 项目的项目经理 Anil Revuru 提供的有关该工具的视频演示 (msdn.microsoft.com/security/ee909463),并且,最好的是,还能下载该工具并亲自试用 (tinyurl.com/3x7bgfd)。
始终检查您的设置
您可能在开发应用程序时遵循了每一个安全开发指南和最佳实践,最终却因为 web.config 配置文件中一个小小错误而受到攻击,这会令人十分沮丧。让人更为恼火的是,您可能意识到 web.config files 设计为随时可更改,以至于在完成应用程序编码并投入生产数年后还会出现配置错误。因此,一直检查您的配置设置是十分重要的,并且不但要进行手动检查,还要使用自动化工具检查,不仅在开发生命周期中检查,还要在生产阶段检查。
正则表达式 DoS 攻击后续信息
另一个完全不同的话题:在 2010 年 5 月的“安全简报”专栏 (msdn.microsoft.com/magazine/ff646973) 中,我写了一篇文章,阐述了 2009 年 9 月的 OWASP Israel 大会上由 Checkmarx 演示的正则表达式 DoS 攻击。在该专栏中,我还提供了基于 Visual Studio 数据库项目数据生成计划功能的正则表达式 DoS 模糊测试程序代码。虽然此方法在技术上看来很完善,也可以顺利检测出正则表达式漏洞,但生成测试数据的过程确实比较枯燥,而且该方法需要您持有 Visual Studio 数据库项目许可证。现在我兴奋地宣布,SDL 团队已经发布了一个新的免费下载工具,使用该工具可以对正则表达式漏洞进行模糊测试,同时为您处理数据生成详细信息。该工具不依赖任何外部平台,只需要 .NET Framework 3.5。如图 3 所示。
图 3 SDL 正则表达式模糊程序
您可以从 microsoft.com/sdl 下载 SDL 正则表达式模糊程序。请试一试,并告诉我们您的想法。
Bryan Sullivan* 是 Microsoft 安全开发生命周期团队的一名安全项目经理,专门负责 Web 应用程序和 Microsoft .NET Framework 的安全问题。他是“Ajax Security”(Addison-Wesley,2007)一书的作者。*
衷心感谢以下技术专家对本文的审阅:Anil Revuru