熊艳芬 發表於 2011-3-5 13:33:09

使用参数化查询防止SQL注入漏洞

<h3>SQL注入的原理</h3>
<p>以往在Web应用程序访问数据库时一般是采取<strong>拼接字符串</strong>的形式,比如登录的时候就是根据用户名和密码去查询:</p>
<p><code><font face="NSimsun">string sql = &quot;SELECT TOP 1 * FROM WHERE UserName = '&quot; + userName + &quot;' AND Password = '&quot; + password + &quot;'&quot;;</font></code></p>
<p>其中userName和password两个变量的值是由用户输入的。在userName和password都合法的情况下,这自然没有问题,但是用户输入是不可信的,一些恶意用户只要用一些技巧,就可以绕过用户名、密码登录。</p>
<p>假设password的值是&quot;<em>1' or '1' = '1</em>&quot;,userName的值随便取,比如是&quot;abc&quot;,那变量sql的值就是:</p>
<p><code><font face="NSimsun">&quot;SELECT TOP 1 * FROM WHERE UserName = 'abc' AND Password = '1' or '1' = '1'&quot;</font></code></p>
<p>由于'1' = '1'恒为真,因此只要User表中有数据,不管UserName、Password的值是否匹配,这条SQL命令准能查出记录来。就这样,登录系统就被破解了。</p>
<h3>以往的防御方式</h3>
<p>以前对付这种漏洞的方式主要有三种:</p>
<ul>
    <li><strong>字符串检测</strong>:限定内容只能由英文、数字等常规字符,如果检查到用户输入有特殊字符,直接拒绝。但缺点是,系统中不可避免地会有些内容包含特殊字符,这时候总不能拒绝入库。 </li>
    <li><strong>字符串替换</strong>:把危险字符替换成其他字符,缺点是危险字符可能有很多,一一枚举替换相当麻烦,也可能有漏网之鱼。 </li>
    <li><strong>存储过程</strong>:把参数传到存储过程进行处理,但并不是所有数据库都支持存储过程。如果存储过程中执行的命令也是通过拼接字符串出来的,还是会有漏洞。</li>
</ul>
<h3>参数化查询</h3>
<p>近年来,自从参数化查询出现后,SQL注入漏洞已成明日黄花。</p>
<p>参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值。</p>
<p>在使用参数化查询的情况下,数据库服务器不会将参数的内容视为SQL指令的一部份来处理,而是在<strong>数据库完成SQL指令的编译后,才套用参数运行</strong>,因此就算参数中含有指令,也不会被数据库运行。Access、SQL Server、MySQL、SQLite等常用数据库都支持参数化查询。</p>
<h4>在ASP程序中使用参数化查询</h4>
<p>ASP环境下的参数化查询主要由<strong>Connection对象</strong>和<strong>Command对象</strong>完成。</p>
<p>Access数据库只支持匿名参数,在传入参数的位置用问号代替即可。SQL Server数据库虽然支持匿名和非匿名的参数,但是在ASP中也仅能使用匿名参数。</p>
<p><code><font face="NSimsun">var conn = Server.CreateObject(&quot;ADODB.Connection&quot;);<br />conn.ConnectionString = &quot;Provider=Microsoft.Jet.OLEDB.4.0;Data Source=&quot; + Server.MapPath(&quot;Test.mdb&quot;);<br />conn.Open();<br /><br />var cmd = Server.CreateObject(&quot;ADODB.Command&quot;);<br />cmd.ActiveConnection = conn;<br />cmd.CommandType = 1;<br />cmd.CommandText = &quot;SELECT TOP 1 * FROM WHERE <strong>UserName = ? AND Password = ?</strong>&quot;;<br />cmd.Parameters.Append(cmd.CreateParameter(&quot;@UserName&quot;, 200, 1, 20, &quot;user01&quot;));<br />cmd.Parameters.Append(cmd.CreateParameter(&quot;@Password&quot;, 200, 1, 16, &quot;123456&quot;));<br /><br />var rs = cmd.Execute();<br />Response.Write(rs(&quot;UserId&quot;).value);<br /><br />rs.Close();<br />conn.Close();</font></code></p>
<h4>在ASP.NET程序中使用参数化查询</h4>
<p>ASP.NET环境下的查询化查询也是通过Connection对象和Command对象完成。如果数据库是SQL Server,就可以用有名字的参数了,格式是<strong>&ldquo;@&rdquo;字符加上参数名</strong>。</p>
<p><code><font face="NSimsun">SqlConnection conn = new SqlConnection(&quot;server=(local)\\SQL2005;user id=sa;pwd=12345;initial catalog=TestDb&quot;);<br />conn.Open();<br /><br />SqlCommand cmd = new SqlCommand(&quot;SELECT TOP 1 * FROM WHERE <strong>UserName = @UserName AND Password = @Password</strong>&quot;);<br />cmd.Connection = conn;<br />cmd.Parameters.AddWithValue(&quot;UserName&quot;, &quot;user01&quot;);<br />cmd.Parameters.AddWithValue(&quot;Password&quot;, &quot;123456&quot;);<br /><br />SqlDataReader reader = cmd.ExecuteReader();<br />reader.Read();<br />int userId = reader.GetInt32(0);<br /><br />reader.Close();<br />conn.Close();</font></code></p>
<p>MySQL的参数格式与SQL Server有点区别,是以<strong>&ldquo;?&rdquo;加上参数名</strong>。</p>
<p><code><font face="NSimsun">MySqlConnection conn = new MySqlConnection(&quot;server=127.0.0.1;uid=root;pwd=12345;database=test;&quot;);<br />conn.Open();<br /><br />MySqlCommand cmd = new MySqlCommand(&quot;SELECT * FROM `User` WHERE <strong>UserName = ?UserName AND Password = ?Password</strong> LIMIT 1&quot;);<br />cmd.Connection = conn;<br />cmd.Parameters.AddWithValue(&quot;UserName&quot;, &quot;user01&quot;);<br />cmd.Parameters.AddWithValue(&quot;Password&quot;, &quot;123456&quot;);<br /><br />MySqlDataReader reader = cmd.ExecuteReader();<br />reader.Read();<br />int userId = reader.GetInt32(0);<br /><br />reader.Close();<br />conn.Close();</font></code></p>
頁: [1]
查看完整版本: 使用参数化查询防止SQL注入漏洞