使用ADO.NET的参数集合来有效防止SQL注入漏洞

SQL注入漏洞是个老话题了,在以前做ASP做开发时,就经常需要用字符串的过虑等方式
来解决这个问题,但有时候确做的不够彻底,往往让黑客钻了空子。
 
那么目前在我们.NET中,不管是用WINFORM开发还是用WEBFORM,连接数据库时都
可以使用ADO.NET,在ADO.NET中,可以设置和获取命令对象的参数来有效的防止SQL
注入问题。不过,在网上查看很多有关ASP.NET的防注入贴子中还有很多是使用以前的
老办法,字符串的过虑,在这里,我们再给大家推荐使用ADO.NET中的参数集合这种方
式,希望能对各位有所帮助。
 
首先我们来了解一下SQL注入漏洞:
SQL注入就是用户通过客户端请求GET或POST方式将SQL语句提交到服务端,欺骗服务
器去执行恶意的SQL语句。比如:
 
做一个用户的登录的SQL语句:
string strSql = "Select * From Users Where UserName='"+name+"' and UserPwd
'"+pwd +"';

如果在登录时输入or 1=1这样的内容的话,name和pwd的值就会跟着改变,那我们来看
 
下这条SQL语句:
SELECT * from Users WHERE UserName = '' or '1'='1' AND UserPwd = '' or '1'='1'
 
这样的话可以直接查出记录,那自然就会登录成功,这样的程序简直是不设防的:),
 
当然,SQL注入漏洞还有很多方式,我们就不在这里详细表述,我们的主题是如何
在.NET的ASP.NET或WINFROM中使用ADO.ENT来解决这个问题。
 
问题的解决办法:
还是我们刚刚的Users表,现在我要写个方法,为该表里插入记录,如下:
public int Insert(string name, string pwd)
{
                string strSql = "Insert into Users(UserName,UserPwd)values('"+name+"','"+pwd+"')";
                OleDbCommand cmd = new OleDbCommand(strSql,Conn);
                Conn.Open();
                int i=cmd.ExecuteNonQuery();
             Conn.Close();
             return i;
}
 
以上的方法中就存在SQL注入的问题,那么如何使用ADO.NET中的叁数集合呢?
接下来再看一段代码:
public int Insert(string name, string pwd)
{
                string strSql = "Insert into Users(UserName,UserPwd)values(@name,@pwd)";
                OleDbCommand cmd = new OleDbCommand(strSql,Conn);
                cmd.Parameters.Add("@name", OleDbType.VarChar).Value = name;
                cmd.Parameters.Add("@pwd", OleDbType.VarChar).Value = pwd;
                Conn.Open();
                int i=cmd.ExecuteNonQuery();
             Conn.Close();
             return i;
}
 
以上的方法中使用了参数集合,这样的话就有效的避免了SQL的注入问题,接下来我们了解一下参数集合:
在ADO.NET的命令对象中,是可以使用参数集合的,如:(例如 Parameters)该集合中包含一组类型为 SqlParameterOleDbParameterOdbcParameterOracleParameter 的对象。对于需要传递的每个参数,集合中均有一个对应的参数对象。
参数的数据类型特定于 .NET Framework 数据提供程序。 如果指定类型,则会在将 Parameter 的值传递给数据源之前,将该值转换为 .NET Framework 数据提供程序类型。 您还可以通用形式指定 Parameter 的类型,方法是将 Parameter 对象的 DbType 属性设置为特定 DbType
Parameter 对象的 .NET Framework 数据提供程序类型是根据 Parameter 对象的 Value 的 .NET Framework 类型或 Parameter 对象的 DbType 推断出来的。
 
有的朋友可能想要知道如何在DataAdapter对象中使用参数,其实也可以用Parameter 来做为参数,做法如下:
public DataTable List(int id)
{
                string strSql="Select * From Users Where id=@id";
                OleDbCommand cmd = new OleDbCommand(strSql,Conn);
                cmd.Parameters.Add("@id", SqlDbType.int).Value = id;
                OledbDataAdapter da= new OledbDataAdapter(cmd);
                DataTable dt = new DataTable();
                da.Fill(dt);    
             retrun dt;
}
 
关于这方面的问题,和Parameter 的使用方式有很多,我们这里只是举了个简单的例子让大家加以应用。
 

简单的给个示例

传统的查询语句的sql可能为
string sql="select * from users where user_id='"+Request.QueryString["uid"]+"'";
很显然,我们在这里拼接了字符串,这就给sql注入留下了可乘之机。

现在,我们要改写这样的语句,使用SqlParameter来做

 

SqlCommand SqlCmd = new SqlCommand(sql, SqlConn);
SqlParameter _userid = new SqlParameter("uid", SqlDbType.Int);
 _userid.Value = Request.QueryString["u_id"];
SqlCmd.Parameters.Add(_userid);

 

这样,我们可以保证外接参数能被正确的转换,单引号这些危险的字符也会转义了,不会再对库造成威胁。

 

如何再编码的过程中防范SQL注入式攻击
1、什么是SQL注入式攻击我 们知道Microsoft的SQL Server数据库是支持一次数据库查询执行多条SQL语句的,比如说再查询管理器里面,我们可以输入一条SQL语句
 select * from table1 select * from table2
假如所选择的数据库中确实存在table1和table2的话,那么这个SQL语句就能够执行成功,并且能够返回正确的结果。同时,我们知道现在很多系统或者发布在Internet上的网站,管理员为了方便,一般都是直接采用sa的身份来连接数据库的,那么SQL注入式攻击主要就是抓住了系统在这么两个方面的弱点,攻击者把SQL语句插入到web表单输入,或者页面的查询字符串中,从而欺骗服务器执行恶意的SQL命令,以达到攻击的目的。
2.   注入式攻击的详细解释SQL下面我们将以一个简单的用户登陆为例,结合代码详细解释一下SQL注入式攻击,与及他的防范措施。对于一个简单的用户登陆可能的代码如下:
try
{
 string strUserName = this.txtUserName.Text;
 string strPwd = this.txtPwd.Text;
 string strSql = "select * from userinfo where UserName="" + strUserName + "" and Password="" + strPwd + """;
 SqlConnection objDbConn = new SqlConnection("数据库连接字符串");
 SqlDataAdapter objAdapter = new SqlDataAdapter(strSql,objDbConn);
 DataSet objDataSet = null;
 objAdapter.Fill(objDataSet);//TODO 对获取的数据进行判断。
}
catch (System.Exception e)
{
 this.lblMsg.Text = e.Message;
 this.lblMsg.Visible = true;
}
  在上面这段代码中,如果用户的输入是正常的用户名和密码的话,那么执行都会比较正常,但是,假如输入用户名的时候,输入的是“johny’--”的话,在 SQLServer里面执行的语句将会是“select * from userinfo where UserName=’johny’--‘ and Password=’密码’”,只要数据库中存在johny这个用户的话,那么不管密码是什么,语句都能够执行成功,并且能够顺利通过登陆。还 有更加厉害的,我们知道SQLServer里面有一些系统的存储过程,能够执行操作系统的很多命令,比如xp_cmdshell,假如上面用户登陆的时 候,用户名部分输入的是“johny’ exec xp_cmdshell ‘format d:/s’--”,大家想想一下后果是什么?有恶意的用户,只要把’format d:/s’这个命令稍加改造就能够做很多不合法的事情啦。

3. 如何防范SQL注入式攻击既然我们解释了为什么会产生SQL的注入式攻击,那么我们接下来说说如何来防范这种情况的发生,解决的办法有很多,我们逐个介绍一下。

a) 在进行系统部署的时候,不要采用sa来连接数据库,在数据库中新建一个帐号,对这个帐号的权限进行限制,只允许在指定的数据库里面执行一下添加,修改的权限,这样我们就可以很好地防止通过执行xp_cmdshell来攻击服务器了。

b) 采用用存储过程来执行所有的查询。SQL参数的传递方式将防止攻击者利用单引号和连字符实施攻击。此外,它还使得数据库权限可以限制到只允许特定的存储过程执行,所有的用户输入必须遵从被调用的存储过程的安全上下文,这样就很难再发生注入式攻击了。在这里大家可能会说,我只是要执行一个简单的查询,难道也要写成一个存储过程吗,这样是不是太麻烦了,其实没有关系,我们只要采用ado.net里面的SqlParameter把上面的代码稍加改造,如下
try
{
 string strUserName = this.txtUserName.Text;
 string strPwd = this.txtPwd.Text;
 string strSql = "select * from userinfo where
UserName=@UserName and Password=@Password";
 SqlConnection objDbConn = new SqlConnection("数据库连接字符串");
 SqlDataAdapter objAdapter = new SqlDataAdapter(strSql,objDbConn);
 objAdapter.SelectCommand.Parameters.Add("@UserName",strUserName);
 objAdapter.SelectCommand.Parameters.Add("@Password",strPwd);
 DataSet objDataSet = null;
 objAdapter.Fill(objDataSet); //TODO 对获取的数据进行判断。
}
catch (System.Exception e)
{
 this.lblMsg.Text = e.Message;
 this.lblMsg.Visible = true;
}
  上面的这段代码,我们就可以比较好地防范SQL 注入式攻击了,原因是SQLServer里面存在一个sp_executesql的系统存储过程,ado里面只要sql语句中用到了 SqlParameter的方式的话,那么sql语句将会通过sp_executesql来执行,具体的情况大家可以用SQL Server里面的Profile来进行跟踪一下,就非常清楚了。通过这种方法,我们就即可以不用写存储过程,有能够很好地防范SQL注入式攻击了对用户的输入进行校验,将用户输入里面存在的单引号进行转义(一个单引号替换成两个单引号),并且过滤里面存在的注释符,与及特殊命令。

 


 

posted @ 2009-12-19 18:31  唔愛吃蘋果  阅读(1702)  评论(0编辑  收藏  举报