使用Asp.Net构建安全网站
摘 要 Asp.Net技术被广泛应用,该技术的安全性越来越受到人们重视。本文介绍黑客对Asp.Net系统的攻击手段以及如何采用Asp.Net技术来避免这些安全漏洞,从而构建安全性的网站系统。
关键字 Asp.Net;安全漏洞;对策
1 引言
Asp.Net又叫ASP+,他不是ASP的简单升级,而是Microsoft推出的新一代Active Server Pages。ASP.Net是建立在微软新一代.Net平台架构上,利用普通语言运行时(Common Language Runtime)在服务器后端为用户提供建立强大的企业级Web应用服务的编程框架。广泛的用在B/S架构的系统构建上。该项技术被国内越来越多的网站所采用。
Asp.Net的安全性逐渐被重视起来。原因很简单,一个安全性的漏洞会对一个网站带来毁灭性的灾难,对网站经营者带来非常严重的损失。保证应用程序的安全,避免安全漏洞应当从编写第一行代码的时候开始做起,这是因为随着应用规模的发展,修补安全漏洞所需的代价也随之快速增长。惠普、IBM、休斯飞机公司、TRW以及其他组织的研究人员发现,在构建活动开始之前清除一个错误,那么返工的成本仅仅是“在开发过程的最后阶段(在系统测试期间或者发布之后)做同样事情”的十分之一到百分之一。
掌握常见的安全漏洞对开发一个系统来说非常必要,利用这些知识点可以用非常低的成本构建安全的网站系统。
2 常见安全漏洞
大多数Web应用程序攻击都要在 HTTP 请求中传递恶意输入项。一般这种攻击并非强迫应用程序执行未经授权的操作,而是要中断应用程序的正常操作。Asp.Net网站系统常见的威胁有代码注入、信息泄漏和身份盗用等。
2.1代码注入
代码注入是最常见的一种安全漏洞,也是最容易受到攻击的,因为攻击者不需要掌握太多的专业知识就可以对这些漏洞进行攻击。对系统的威胁也非常大。代码注入攻击主要包括SQL 注入、跨站点脚本和缓冲区溢出三种:
SQL注入(SQL injection):根据用户的输入值来动态构造SQL语句,该构造语句很可能就是攻击性的有害SQL语句。即攻击者可发送SQL输入来更改数据库中的预期查询或执行全新的查询。表单身份验证登录页是常见的攻击对象,因为查询用户存储所使用的是用户名和密码。
跨站点脚本(Cross-site Scripting):将恶意脚本作为输入项发送到 Web 应用程序。一旦执行,结果将回应至用户浏览器。
缓冲区溢出(Buffer Overflow):虽然托管代码的类型安全验证可大大降低风险,但应用程序依然存在安全漏洞,特别是调用非托管代码时。缓冲区溢出使攻击者可利用 Web 应用程序的安全上下文在 Web 应用程序进程中执行任意代码。
这里我们要特别搞清楚一个概论,传输加密技术不能用来防御注入攻击。注入攻击可通过使用HTTP或HTTPS Secure Socket Layer(SSL) 连接发送请求。收到信息的服务器会认为该信息是合法的输入。
2.2信息泄漏
信息泄漏主要涉及把一些服务器出错信息完全透露给请求者和隐藏域信息被攻击者使用等。攻击者往往通过探测 Web页来找寻引起异常的各种情况,异常细节信息常以 HTML 的形式返回并显示在浏览器中。这可能会泄漏很有用的信息,如堆栈跟踪。堆栈跟踪包含数据库连接字符串、数据库名、数据库方案信息、SQL 语句以及操作系统和平台版本。攻击者利用这些信息很容易找到攻击的方法。
2.3身份盗用
恶意用户盗用合法用户的身份访问网站系统,主要包括身份验证Cookie未加密的保存在客户端,系统中用户使用了弱密码等。攻击者采用这些信息以合法的用户身份登录系统。
3 预防措施
知道了攻击者怎么攻击Asp.Net网站系统,系统在编写第一句代码的时候就需要考虑安全性问题,防止系统漏洞的出现。不给攻击者攻击的机会。
3.1防Sql注入
先来看一下以下语句:
SqlDataAdapter UserQuery = new SqlDataAdapter( "Select * From Users Where UserName='" + username + "' And Password='" + password, conn);
验证登录用户名和密码,正常输入没有什么问题。但如果username输入“Or 1=1”,密码无论输入什么都可以进入系统。更为可怕的是如果连接数据库的用户权限足够大,还可以通过“;exec master..xp_cmdshell 命令 –-” 执行任意Shell命令。
对于SQL注入的预防,可以通过很多种技术来解决。
第一:对于一定需要动态构造SQL查询的场合,可以通过格式化用户输入的信息来避免SQL注入,即可以替换输入信息中的单引号,即把所有单独出现的单引号改成两个单引号,防止攻击者修改SQL命令的含义;可以删除用户输入内容中的所有连字符,防止攻击者构造出类如“Select * From Users Where UserName = 'Dean' -- And Password =''”之类的查询,因为这类查询的后半部分已经被注释掉,不再有效,攻击者只要知道一个合法的用户登录名称,根本不需要知道用户的密码就可以顺利获得访问权限。
第二:用存储过程来执行所有的查询。SQL参数的传递方式将防止攻击者利用单引号和连字符实施攻击。此外,它还使得数据库权限可以限制到只允许特定的存储过程执行,所有的用户输入必须遵从被调用的存储过程的安全上下文,这样就很难再发生注入式攻击了。
第三:检查用户输入的合法性,确信输入的内容只包含合法的数据,并限制表单或查询字符串输入的长度。数据检查应当在客户端和服务器端都执行——之所以要执行服务器端验证,是为了弥补客户端验证机制脆弱的安全性。
第四:限制用来执行查询的数据库帐户权限,不让其执行一些特殊的存储过程和访问不该访问的资源。对查询所返回的记录数量也作一些判断,如程序只要求返回一个记录,但实际返回的记录却超过一行,那就当作出错处理。
3.2不要相信用户输入
在Web应用开发中,开发者最大的失误往往是无条件地信任用户输入,假定用户(即使是恶意用户)按正常情况进行操作并总是受到浏览器的限制,总是通过浏览器和服务器交互,给攻击者打开了方便之门。
<%@ Page Language="C#" ValidateRequest="false" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = TextBox1.Text;
}
</script>
<form runat="server">
<asp:Label ID="Label1" runat="server"/>
<asp:TextBox ID="TextBox1" runat="server"/>
<asp:Button ID="Button1" OnClick="Button1_Click" runat="server" Text="提交"/>
</form>
上面的代码简单,但却有一个非常严重的安全漏洞。比如用户输入“<script>alert(document.cookie)</script>”。堵住该漏洞需要使用ASP.NET请求验证,在页面的@Pages元素里面设置ValidateRequest = "True"来打开请求验证,默认情况下Asp.Net 1.1和2.0该项设置是打开的。ASP.NET请求验证会对送至服务器的数据检测是否含有HTML标记元素和保留字符。这可以防止用户向程序中输入脚本。请求验证会对照一个有潜在威胁的字符串列表进行匹配,如果发现异常它会抛出一个HttpRequestValidationException类型的异常。
有些情况,需要准许用户输入特定HTML代码。这就需要过滤输入数据使它们在输出的时候没有危险,使用HttpUtility.HtmlEncode方法先对输入值进行编码,再替换特定的HTML标记。例如前面的例子,如果准许用户输入<b>标记:
StringBuilder sb = new StringBuilder(HttpUtility.HtmlEncode(TextBox1.Text));
sb.Replace("<b>", "<b>");
sb.Replace("</b>", "</b>");
对不安全的URL地址也要使用HttpUtility.UrlEncode进行编码。因此,只有严密地验证用户输入的合法性,才能有效地抵抗黑客的攻击。
3.3让隐藏域更安全
在ASP.NET应用中,几乎所有HTML页面都有__VIEWSTATE隐藏域,它储存着来自页面控件的动态数据、开发者在ViewState中显式保存的数据以及它们数据的密码签字。如图1所示:
图1:页面__VIEWSTATE值
由于__VIEWSTATE是BASE 64编码的,所以往往被忽略,但黑客可以方便地解码BASE 64数据,轻松的得到__VIEWSTATE提供的详细资料。通过设置EnableViewStatMAC="True"来加密ViewState数据。启用__VIEWSTATE数据加密功能,可以在页面级别上设置 EnableViewStateMAC,也可以在应用程序级别上设置。然后还需将machineKey验证类型设置成3DES,要求ASP.NET用3DES对称加密算法加密ViewState数据。
3.4加密身份验证
ASP.NET身份验证方式有"Windows"、 "Forms"和"Passport" 。"Forms"可以为用户提供一个输入凭据的登录窗体,即平常见到的输入用户名和密码窗体。出于身份验证的灵活性和系统独立性,多数Web应用程序都采用Form验证方式。
攻击者可以利用 XSS 窃取身份验证的Cookie进行查看或修改。为了增强Cookie 的隐私性和完整性,可以在 <forms> 元素中设置 protection 属性,如下:
<forms protection="All"
指明应用程序同时使用数据验证和加密方法来保护Cookie。第二限制身份验证Cookie 的生命周期,可以减少攻击者使用盗用的 cookie 骗取应用程序访问权限的时间窗口。如下:
<forms timeout="10"
最后在 <forms> 元素中使用唯一的 name 和 path 属性值。如果名称唯一,可在同一服务器驻留多个应用程序时防止出现潜在的问题。例如,如果不使用独特的名称,通过某一应用程序身份验证的用户无需重定向至另一应用程序的登录页即可请求访问该应用程序。
另外在创建身份验证票据时也应该加密票据,有效防止身份被窃取。如下:
System.Web.Security.FormsAuthenticationTicket tk=new System.Web.Security.FormsAuthenticationTicket(1,"Admin",System.DateTime.Now, System.DateTime.Now.AddYears(1),false,"测试用户数据");
String str=System.Web.Security.FormsAuthentication.Encrypt(tk);//加密身份验证票据
3.5防止信息泄漏
攻击者在攻击网站应用系统前,首先需要获取网站应用系统的信息。他们通常向Web应用服务器抛出很多请求,引发Web错误抛出异常,从而获得很多对攻击者有用的信息。
在部署网站的时候,不要把错误信息返回给客户端,可以在Web.config使用<customErrors>元素来配置,一般的错误信息应该被程序错误检测机制返回到客户端。确认已经更改customErrors中的mode属性为"RemoteOnly",指明IIS只对不在本地 Web 服务器上运行的用户显示自定义(友好的)信息。代码示例为:
<customErrors mode = "RemoteOnly">
也可以通过设置defaultRedirect指定自定义错误页,如下:
<customErrors mode = "on" defaultRedirect = "Error.htm">
另外一方面,网站的数据库连接字符串经常写在Web.config配置文件里面。但是很多网站往往是直接把连接字符串写入,用户名和密码以明文的方式保存。也就是拿到配置文件就相当于完全掌握了数据库。可以通过System.Security.Cryptography在加密存储。需要用得时候在通过它解密。
4 结束语
知道系统的安全漏洞在什么地方,在开发系统时编写第一句代码就考虑安全性问题。采用Asp.Net的先进技术,防止漏洞的出现。随着Asp.Net技术的广泛应用,网站的逐步普及。如何采用Asp.Net构建安全性网站必将受到人们的高度重视。
参考文献:
[1]http://www.microsoft.com/china/msdn.
[2]http://www.cnblogs.com.