表 10 EncryptCnxString.aspx
您可以使用EncryptCnxString.aspx页面创建计算机特有的加密连接字符串贴到你的配置文件中。这个页面显示所表10。当然,除了有连接字符串的密码和其他机密,您可能要加密或哈希,包括信用卡号码和其他任何可能造成损害的信息,否则他们可能会被泄露给黑客所知道。ASP.NET2.0包含了一些新特性,它简化了哈希密码和对连接字符串的加密。
优化错误信息
运行时的异常错误处理不当将成为被黑客利用的另一片领域。所以在你的代码产品中加入异常处理变得非常重要。处理的和未处理的异常信息应该提供最少的对黑客有利的信息。
此文首发于:http://www.qhwins.com/ShowNews/?11-200910271239038948.html 转载请注明出处
所有的用户输入都是不可靠的
图4中的第一个原则是非常重要的,假设所有的用户输入都非常不可靠!你永远不要把用户的输入信息没有通过验证就带到数据库中进行查询。ASP.NET验证控件,特别是正则表达式验证控件是一个验证用户输入的好工具.有两种最简单的验证方式:禁止危险字符和允许所需要的字符.虽然你可以很EASY的禁止一些危险字符,如单引号和连字符,但是这样还不是最佳方法,有两个原因:第一,你可能在过滤时会漏掉一些对黑客很有用的字符,第二,通常,我们可以用多种方法来表示这个危险字符,来绕过过滤.比如黑客通过escaped一个单引号的方法绕过单引号限制,直接把它带到数据库中进行查询. Escaped的单引号在数据库中进行查询时,它和正常的单引号是没有任何区别的.这样就逃过了你的代码验证.更好的方法是只确实允许的字符.虽然这种方法加大了工作量,但是却保证了更好的安全性.除了上述的方法外,你还应该限定输入字符串的最大长度,因为黑客在攻击的时候,往往会需要输入大量的字符串.GoodLogin.aspx(在下载中可以找到)包含了两个正则表达式验证控件。一个是验证用户名的,另一个用来验证密码,让它限定在4-12个由字符、数字和下划限组成的字符串。
[\d_a-zA-Z]{4,12}
有时候我们可能需要让客户输入一些危险的过滤字符,举例, person’s name 中的单引号。这种情况下,你可以使用regular expression 或是 String.Replace 方法来将每个单引号替换成两个单引号。举例:
string strSanitizedInput = strInput.Replace("'", "''");
注入测试 从测试的角度来看,我们重要的是要认识到,SQL注入的主要目标是企图通过改变被发送到SQL服务端查询的语句来控制SQL服务器。测试此漏洞前,你首先要知道如何利用这个漏洞,然后自己测试下面的方法: 寻找漏洞 黑客们开始在网站寻找漏洞。这些漏洞可能是任何可以接收输入,查找,提交表单,或是ASP,JSP,CGI或PHP页面,它们也包括隐藏域,而并不是局限在显示在页面上的域[译者加:域是指提交表单,输入框这类] 测试漏洞是否真的存在 为了测试漏洞是否可以成功利用,黑客们最常用的方法是单引号和撇号{apostrophe},[译者加:apostrophe翻译出来是撇的意思,其实应该是指分号";",因为分号可以将新的语句跟到前面的语句来执行,即多语句查询,以此为攻击。注,SQL SERVER支持多语句查询,ACCESS不支持]这两个字符是SQL语句中用来做分隔符之用。如果用户的输入没有通过验证而直接到达了数据库进行操作,那么搞定这个数据库管理系统将变得很简单。为了测试这样的漏洞,黑客们可能将 jo'hn 这样的代码输入到一个输入框中,或是输入到这样的一个地址中http://www.qhwins.com/index.asp?id=jo'hn 。 如果黑客利用的是代码中隐藏的一个域的话,这点小麻烦对黑客来说没算什么,它可以把源代码下载并保存下来,然后修改URL,隐藏字段,再去提交执行。 如果这是一个真正的弱点,撇号后的其他信息,将被视为一提交到SQL服务器的查询字符串的一部分,将由后端执行。 错误描述 如果黑客要找什么,哪么错误页对于黑客来说,将是帮助他们的一个很好的判断工具,因为错误页提供了黑客想要知道的一些信息。如果一个ODBC的错误信息被返回,黑客就确实了,这真的是一个可以利用的漏洞。这些错误是由数据库系统生成的,这也意味着单引号被成功带入到后台进行了查询。 继续测试 黑客们将继续尝试绕过网站的验证和其它过滤程序,认真的审查每条服务器的响应输出情况,这些努力包括: 改变单引号在不同的地方出现,试图在任何例程中有机可乘。例如,电子邮件域可能会验证一个@符号和一个句点。如果黑客输入joe'@qhwins.com ,这样将会验证失败,但是joe@qhwins.com' 将会成功,并发现漏洞。 在字符串最大长度的末尾上加一个单引号。如果网站转义了单引号,[原:If the site is escaping single quotes, an attempt to escape the single quote could result in truncation back to the single quote.] 使用连字符(破折号)。在SQL中,这意味后是单行注释,并可能导致服务器忽略该行的其余部份。 使用分号。这个表明后面跟的是一个新的查询,可以允许这个新查询在第一条语句后面被执行。 使用高级的unicode字符,它们经常被降级到ASCII “当量”,包括危险的单引号。[原:Using high Unicode characters that are often downgraded to ASCII "equivalents," including the dangerous single quote.] 在所有的字段域中使用这些技术,不光包括字符串域,而且包括任何隐式转换的地方和仅仅通过UI格式执行的域。 使用#字符。有时你会觉得这是一个日期/时间的分隔符使用。 使用等值的危险字符。
避免动态SQL
我在本文中演示的SQL注入攻击都是依赖在动态生成的SQL上执行的,即最终SQL语句是通过和用户输入的值进行相连接而成的。但是,使用参数化SQL,将会大大降低了黑客注入你代码的能力。在表5中的代码,员工employs的参数化SQL阻止了SQL注入的攻击。如果你必需使用ad hoc SQL(译者加:高级特性的SQL?是缩写,大家自己理解),参数化SQL将是很有用的。这可能是必要的,如果你的IT部门不相信存储过程或使用诸如MYSQL,MYSQL只至5.0才支持存储过程。无论如何,你应该使用存储过程来增加 删除所有基表权限和创建查询,像表3的示。BetterLogin.aspx,如表6所示,使用了一条存储过程,procVerifyUser来验证客户。[这段译的不太好,请自己看原文]
表6 BetterLogin.aspx.cs
private void cmdLogin_Click(object sender, System.EventArgs e) { string strCnx = ConfigurationSettings.AppSettings["cnxNWindBetter"]; using (SqlConnection cnx = new SqlConnection(strCnx)) { SqlParameter prm; cnx.Open(); string strAccessLevel; SqlCommand cmd = new SqlCommand("procVerifyUser", cnx); cmd.CommandType= CommandType.StoredProcedure; prm = new SqlParameter("@username",SqlDbType.VarChar,50); prm.Direction=ParameterDirection.Input; prm.Value = txtUser.Text; cmd.Parameters.Add(prm); prm = new SqlParameter("@password",SqlDbType.VarChar,50); prm.Direction=ParameterDirection.Input; prm.Value = txtPassword.Text; cmd.Parameters.Add(prm); strAccessLevel = (string) cmd.ExecuteScalar(); if (strAccessLevel.Length>0) { FormsAuthentication.RedirectFromLoginPage(txtUser.Text, false); } else { lblMsg.Text = "Login attempt failed."; } } } |
表 5 GoodLogin.aspx.cs
private void cmdLogin_Click(object sender, System.EventArgs e) { string strCnx = ConfigurationSettings.AppSettings["cnxNWindBad"]; using (SqlConnection cnx = new SqlConnection(strCnx)) { SqlParameter prm; cnx.Open(); string strQry = "SELECT Count(*) FROM Users WHERE UserName=@username " + "AND Password=@password"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); cmd.CommandType= CommandType.Text; prm = new SqlParameter("@username",SqlDbType.VarChar,50); prm.Direction=ParameterDirection.Input; prm.Value = txtUser.Text; cmd.Parameters.Add(prm); prm = new SqlParameter("@password",SqlDbType.VarChar,50); prm.Direction=ParameterDirection.Input; prm.Value = txtPassword.Text; cmd.Parameters.Add(prm); intRecs = (int) cmd.ExecuteScalar(); if (intRecs>0) { FormsAuthentication.RedirectFromLoginPage(txtUser.Text, false); } else { lblMsg.Text = "Login attempt failed."; } } } |