领先技术: 子类化和重写 ASP.NET 页面 - 第 II 部分 -- MSDN Mag...
来源:百度文库 编辑:神马文学网 时间:2024/10/06 16:42:21
领先技术
子类化和重写 ASP.NET 页面 - 第 II 部分
Dino Esposito
more ...
打印
E-mail
添加到收藏夹
评价
RSS (领先技术)
Add RSS to Any
相关信息
Live Spaces
Digg This
BlogThis!
Slashdot
del.icio.us
Technorati
Explore the code.
Download the code.
Get the sample code for this article. NEW:Explore the sample code online!
- or -
代码下载位置:CuttingEdge2007_05.exe (163KB)
目录
背景知识
原始解决方案
模拟一次很简单的攻击
其中的原理是什么呢?
改进后的 TextBox 控件
替换 TextBox 控件
最终的解决方案
最近一个很偶然的机会,我发现了一个大型网站,上面全是一些极其简单的 Web 用户控件,确切地说是一些 ASCX 文件。开发人员在发现所使用的服务器控件会出现异常行为后,往往认为这种方法是很有必要的。
因此,开发人员将站点内的这类服务器控件全部更换为包含原始控件修改版本的用户控件(同时由于无法确定更换服务器控件会导致何种后果,因此开发人员还替换了其他大量控件。)开发人员认为,将这样一种额外的抽象层置于页面和控件之间会更可靠。另外一个好处就是可以在 ASP.NET 应用程序中轻松替换用户控件(如果需要的话),而无需修改二进制文件和重新启动应用程序。(这种情况并非始终都会发生,但有些部署方案会要求执行该操作。)
曾经有公司请我来审阅应用程序,他们问我的第一个问题就是:“是否有更好的方法可以在不大量返工每个页面的情况下替换整个站点的服务器控件?”
我在自己主持的2007 年 4 月专栏中,针对如何在不修改原始源代码的情况下对 ASP.NET 网站进行有限的(有时是临时的)修改给出了几种解决方案。本月我又发现几种技巧,无需修改源代码,通过声明的方式即可替换服务器控件和 URL。
当时我无法立即回答他们的问题,但却知道如何找到解决方法。我想如果是我开发了 ASP.NET 基础结构,我会在配置文件中放置某种设置,以便开发人员能够通过声明的方式将标记映射到控件。在 ASP.NET 中这并非是一个全新的理念。早在 ASP.NET 1.x 中,您就可以通过声明的方式更改一些与代码相关的内容,例如网页和用户控件的基类。(但是,这种方法只适用于未在 Page 指令中显式使用 Inherits 子句的页面和用户控件。)因此我产生了一个疑问,为什么服务器控件不可以采取这种方法呢。事实证明我当时的推断是正确的:ASP.NET 2.0 正是为此才提供了 节。
背景知识
我想还是先向大家介绍一些背景知识。此方案始于一次内部安全审查,当时客户发现应用程序内存在一个可能导致经典 SQL 注入式攻击的漏洞。公司对这一漏洞应用了快速修补程序,但却导致了另一个问题。
在客户的网站上,许多页面都允许查询字符串中包含固定的五字符代码。这种代码会随后用于构成 SQL 语句。该公司当时仍在运行类似以下内容的代码:
Dim code As String = Request.QueryString(“Code”).ToString();Dim command As String = _“SELECT * FROM customers WHERE id=’” & code & “’”说心里话,我真的希望您的网站已经不再运行类似代码!这种代码完全盲目地信任任何通过查询字符串传递的信息,并会将该信息附加到构成 SQL 命令的字符串。这样做会形成非常严重的安全隐患。手段高明的黑客能够轻而易举地发现那些看似正常、实则危险的文本,它们能够将原始的和为特定目的编写的 SQL 命令变成危险的攻击。如果您需要更多有关 SQL 注入的详细信息,建议您先阅读 Paul Litwin 撰写的文章“Stop SQL Injection Attacks Before They Stop You”。
从性能方面考虑,发送动态构建的命令是不明智的。这些命令不会从重用查询计划中获益,因为代码本身在每次提交时都可能发生变化。使用参数化查询或存储过程有两大优势:一是它能确保至少对发送的数据类型进行一次自动检查,二是它能够从 SQL Server™ 和其他数据库中的查询计划缓存中获得好处。
正如“领先技术”2007 年 3 月刊中所述,您应该像验证控制台实用程序的命令行那样静态地对查询字符串进行验证。这样可以避免数字、日期和布尔值的歧义,但如果是字符串,您就无可奈何了。在这里,问题的关键是字符串中到底包含哪些内容。您需要使用某种业务逻辑仔细地验证传递的字符串。但验证字符串长度并不难,不管是通过 HTTP 模块还是通过有限地修改代码,都是可以实现的。
原始解决方案
发现 SQL 注入漏洞后,客户认为将可接受参数的大小限制为五个字符(即代码的实际大小)就能快捷有效地解决问题。仅有五个可用的字符,黑客是奈何不了您的数据库– 至少希望是这样的。因此,客户安装了“领先技术”2007 年 3 月刊中演示的 HTTP 模块,并检查了受影响页面的查询字符串大小。结果发现实际发送到页面的字符未超过五个。
但是,该应用程序组合了新的 ASP.NET 页面和经过修改的经典 ASP 页面,其中某些页面能够允许用户在文本框内键入并提交相同的代码。而在服务器上,指定的代码会在回发过程中通过前述的相同方法附加到 SQL 语句中。因此,通过文本框提交的文本长度也需要进行同样的限制。开发人员原以为这是个简单的问题,因此将文本框的 MaxLength 属性设置为所需的值:
修复过的问题看上去万无一失。长度超过五个字符的代码无法进入站点的中间层。但这并不一定意味着站点处于可避免注入的安全状态,但这种做法确实限制了遭受攻击的可能性。或者说他们是这么认为的。
模拟一次很简单的攻击
假设有一个类似于图 1 所示的 ASP.NET 示例页面。页面的源代码如图 2 所示。该页面具有一个 MaxLength 属性为 5 的文本框和一个提交按钮。单击按钮后,会执行回发操作并对文本框的内容进行处理。正常情况下,在浏览器中显示页面的位置是无法键入五个以上的字符的。如果您尝试粘贴更长的文本字符串,字符串将被相应地截断为指定长度。
图 1 ASP.NET 示例页面 (单击该图像获得较小视图)
图 1 ASP.NET 示例页面 (单击该图像获得较大视图)
现在我们从攻击者的角度考虑这个问题。对网页进行攻击通常需要先创建格式为 Plain HTML 的页面副本,然后改变某些值并发布“破坏”版的页面。要获得页面的 HTML,恶意用户只需向普通用户那样显示页面:选择“查看源代码”,然后将内容保存为本地 HTML 文件。但这种方法只有在攻击者可以实际访问页面时才可能奏效。例如,如果页面受到保护,攻击者就必须出示有效凭据才能查看页面。但是,被盗用的身份验证 Cookie、网络钓鱼诈骗及其他社交诈骗术都可以使有用的信息流入不正当人的手中。
将 ASP.NET 页的标记保存到本地机器后,攻击者需要对其进行一些更改。首先,攻击者必须更改表单的 action 属性,使其指向同一 ASP.NET 页面的绝对 URL。以下是 ASP.NET 的 default.aspx 页面的服务器表单的典型标记:
子类化和重写 ASP.NET 页面 - 第 II 部分
Dino Esposito
Download the code.
- or -
代码下载位置:CuttingEdge2007_05.exe (163KB)
背景知识
原始解决方案
模拟一次很简单的攻击
其中的原理是什么呢?
改进后的 TextBox 控件
替换 TextBox 控件
最终的解决方案
最近一个很偶然的机会,我发现了一个大型网站,上面全是一些极其简单的 Web 用户控件,确切地说是一些 ASCX 文件。开发人员在发现所使用的服务器控件会出现异常行为后,往往认为这种方法是很有必要的。
因此,开发人员将站点内的这类服务器控件全部更换为包含原始控件修改版本的用户控件(同时由于无法确定更换服务器控件会导致何种后果,因此开发人员还替换了其他大量控件。)开发人员认为,将这样一种额外的抽象层置于页面和控件之间会更可靠。另外一个好处就是可以在 ASP.NET 应用程序中轻松替换用户控件(如果需要的话),而无需修改二进制文件和重新启动应用程序。(这种情况并非始终都会发生,但有些部署方案会要求执行该操作。)
曾经有公司请我来审阅应用程序,他们问我的第一个问题就是:“是否有更好的方法可以在不大量返工每个页面的情况下替换整个站点的服务器控件?”
我在自己主持的2007 年 4 月专栏中,针对如何在不修改原始源代码的情况下对 ASP.NET 网站进行有限的(有时是临时的)修改给出了几种解决方案。本月我又发现几种技巧,无需修改源代码,通过声明的方式即可替换服务器控件和 URL。
当时我无法立即回答他们的问题,但却知道如何找到解决方法。我想如果是我开发了 ASP.NET 基础结构,我会在配置文件中放置某种设置,以便开发人员能够通过声明的方式将标记映射到控件。在 ASP.NET 中这并非是一个全新的理念。早在 ASP.NET 1.x 中,您就可以通过声明的方式更改一些与代码相关的内容,例如网页和用户控件的基类。(但是,这种方法只适用于未在 Page 指令中显式使用 Inherits 子句的页面和用户控件。)因此我产生了一个疑问,为什么服务器控件不可以采取这种方法呢。事实证明我当时的推断是正确的:ASP.NET 2.0 正是为此才提供了
背景知识
我想还是先向大家介绍一些背景知识。此方案始于一次内部安全审查,当时客户发现应用程序内存在一个可能导致经典 SQL 注入式攻击的漏洞。公司对这一漏洞应用了快速修补程序,但却导致了另一个问题。
在客户的网站上,许多页面都允许查询字符串中包含固定的五字符代码。这种代码会随后用于构成 SQL 语句。该公司当时仍在运行类似以下内容的代码:
Dim code As String = Request.QueryString(“Code”).ToString();Dim command As String = _“SELECT * FROM customers WHERE id=’” & code & “’”说心里话,我真的希望您的网站已经不再运行类似代码!这种代码完全盲目地信任任何通过查询字符串传递的信息,并会将该信息附加到构成 SQL 命令的字符串。这样做会形成非常严重的安全隐患。手段高明的黑客能够轻而易举地发现那些看似正常、实则危险的文本,它们能够将原始的和为特定目的编写的 SQL 命令变成危险的攻击。如果您需要更多有关 SQL 注入的详细信息,建议您先阅读 Paul Litwin 撰写的文章“Stop SQL Injection Attacks Before They Stop You”。
从性能方面考虑,发送动态构建的命令是不明智的。这些命令不会从重用查询计划中获益,因为代码本身在每次提交时都可能发生变化。使用参数化查询或存储过程有两大优势:一是它能确保至少对发送的数据类型进行一次自动检查,二是它能够从 SQL Server™ 和其他数据库中的查询计划缓存中获得好处。
正如“领先技术”2007 年 3 月刊中所述,您应该像验证控制台实用程序的命令行那样静态地对查询字符串进行验证。这样可以避免数字、日期和布尔值的歧义,但如果是字符串,您就无可奈何了。在这里,问题的关键是字符串中到底包含哪些内容。您需要使用某种业务逻辑仔细地验证传递的字符串。但验证字符串长度并不难,不管是通过 HTTP 模块还是通过有限地修改代码,都是可以实现的。
原始解决方案
发现 SQL 注入漏洞后,客户认为将可接受参数的大小限制为五个字符(即代码的实际大小)就能快捷有效地解决问题。仅有五个可用的字符,黑客是奈何不了您的数据库– 至少希望是这样的。因此,客户安装了“领先技术”2007 年 3 月刊中演示的 HTTP 模块,并检查了受影响页面的查询字符串大小。结果发现实际发送到页面的字符未超过五个。
但是,该应用程序组合了新的 ASP.NET 页面和经过修改的经典 ASP 页面,其中某些页面能够允许用户在文本框内键入并提交相同的代码。而在服务器上,指定的代码会在回发过程中通过前述的相同方法附加到 SQL 语句中。因此,通过文本框提交的文本长度也需要进行同样的限制。开发人员原以为这是个简单的问题,因此将文本框的 MaxLength 属性设置为所需的值:
修复过的问题看上去万无一失。长度超过五个字符的代码无法进入站点的中间层。但这并不一定意味着站点处于可避免注入的安全状态,但这种做法确实限制了遭受攻击的可能性。或者说他们是这么认为的。
模拟一次很简单的攻击
假设有一个类似于图 1 所示的 ASP.NET 示例页面。页面的源代码如图 2 所示。该页面具有一个 MaxLength 属性为 5 的文本框和一个提交按钮。单击按钮后,会执行回发操作并对文本框的内容进行处理。正常情况下,在浏览器中显示页面的位置是无法键入五个以上的字符的。如果您尝试粘贴更长的文本字符串,字符串将被相应地截断为指定长度。
图 1 ASP.NET 示例页面 (单击该图像获得较小视图)
图 1 ASP.NET 示例页面 (单击该图像获得较大视图)
现在我们从攻击者的角度考虑这个问题。对网页进行攻击通常需要先创建格式为 Plain HTML 的页面副本,然后改变某些值并发布“破坏”版的页面。要获得页面的 HTML,恶意用户只需向普通用户那样显示页面:选择“查看源代码”,然后将内容保存为本地 HTML 文件。但这种方法只有在攻击者可以实际访问页面时才可能奏效。例如,如果页面受到保护,攻击者就必须出示有效凭据才能查看页面。但是,被盗用的身份验证 Cookie、网络钓鱼诈骗及其他社交诈骗术都可以使有用的信息流入不正当人的手中。
将 ASP.NET 页的标记保存到本地机器后,攻击者需要对其进行一些更改。首先,攻击者必须更改表单的 action 属性,使其指向同一 ASP.NET 页面的绝对 URL。以下是 ASP.NET 的 default.aspx 页面的服务器表单的典型标记: