SQL语句中的单引号处理以及模糊查询
为了防⽌程序SQL语句错误以及SQL注⼊,单引号必须经过处理。有2种办法:
1、使⽤参数,⽐如SELECT * FROM yourTable WHERE name = @name;
在C#中使⽤SqlParameter parameter = new SqlParameter("@name", objValue);来添加参数,懒得写SqlDbType这东西了,因为不写也完全可以,只需要参数名和值。
在JAVA中就是⽤预处理PreparedStatement来添加参数。
2、如果不⽤参数,⽽⽤字符串拼接的话,单引号必须经过判断并替换,在数据库中,⽤2个单引号代表1个实际的单引号。所以,如果是拼接⽅式,需要⽤String.Replace("'", "''")来替换⼀下,将1个单引号替换为2个就没有问题了。
SQL语句中,字符串是⽤两个单引号包起来标⽰的,所以要在字符串⾥保留单引号,必须要转义,⽽转义很简单,就是两个连续的单引号就表⽰⼀个单引号字符。
再说⼀下C#中的模糊查询,为了避免单引号,我们使⽤参数的⽅式,下⾯的语句是不对的:
SELECT * FROM yourTable WHERE name LIKE ‘%@name%’;在这个句⼦中,’%@name%’被整体当作⼀个字符串来处理,你⽆论如何查询不到结果。修改⼀下,SELECT * FROM yourTable WHERE name LIKE @name;然后添加参数的时候这么添加:
new SqlParameter("@name", "%" + categoryName + "%"); 这下就没问题了,正常查询,你输单引号照样查。
⼀. SQL Injection及其防范的基本知识
可能⼤家都知道,SQL注⼊主要是利⽤字符型参数输⼊的检查漏洞。
⽐如说,程序中有这样的查询:
string sql = "SELECT * FROM SiteUsers WHERE UserName=" + userName + "";
其中的userName参数是从⽤户界⾯上输⼊的。
如果是正常的输⼊,⽐如"Peter",SQL语句会串接成:
"SELECT * FROM SiteUsers WHERE UserName=Peter";
如果攻击者输⼊的是下⾯的字符串:
"xxx; DROP TABLE SiteUsers WHERE 1=1 or UserName=xxx"
此时SQL语句会变成下⾯这个样⼦:
"SELECT * FROM SiteUsers WHERE UserName=xxx; DROP TABLE SiteUsers WHERE 1=1 or UserName=xxx";
其结果,得到执⾏的是两个SQL语句,第⼆个语句的后果就⽐较严重了。
防⽌注⼊的⽅法其实很简单,只要把⽤户输⼊的单引号变成双份就⾏了:
string sql = "SELECT * FROM SiteUsers WHERE UserName=" + userName.Replace("","") + "";
这样,如果输⼊的是上⾯那种恶意参数,整个SQL语句会变成:
"SELECT * FROM SiteUsers WHERE UserName=xxx; DROP TABLE SiteUsers WHERE 1=1 or UserName=xxx";
被执⾏的还是⼀个SQL语句,整个粗体部分都成为参数值。
⼀般的做法,是在程序中统⼀调⽤下⾯这样的共通函数,对参数进⾏处理:
private string SafeSqlLiteral(string inputSQL)
{
return inputSQL.Replace("'", "''");
}
由于很多⼈会疏忽这种单引号替换,所以真正安全的做法是使⽤参数化查询。
⼆. 参数化查询
在ADO.NET中,提供了⼀种参数化查询⽅法,可以替代上⾯这种拼接SQL语句的做法。
参数化查询的具体实现是:
(1)组织⼀个夹带参数名的SQL语句,作为SqlCommand的CommandText。
(2)使⽤Parameters.Add⽅法设置参数值。
(3)执⾏SqlCommand。(这个步骤跟上⾯那种拼接SQL的办法是⼀样的。)
下⾯是⼀个例⼦:
string sql = "SELECT T2.dep_code, T2.dep_name FROM DEP ";
sql += " WHERE T2.dep_name like (%+ @Param + %) ";
SqlCommand sqlCommand = new SqlCommand(sql,cn);
sqlCommand.Parameters.Add(new SqlParameter("Param", s));
其中的@Param就是参数名,s则是⽤户输⼊的查询条件字串。
(顺便注:Oracle查询语句参数⽤问号表⽰,不是"@参数名"的形式。)
使⽤这种参数化查询的办法,防⽌SQL注⼊的任务就交给ADO.NET了。
如果在项⽬中统⼀规定必须使⽤参数化查询,就不⽤担⼼因个别程序员的疏忽导致的SQL注⼊漏洞了。
但是,问题还没有完,SQL注⼊的漏洞是堵住了,但是查询结果的正确性,参数化查询并不能帮上什么忙。
三. 通配符问题
如果使⽤LIKE语句进⾏模糊查询,会有⼀些特殊的通配符问题。
SQL Server的通配符包括下划线(_)和百分号(%),分别表⽰单个字符和任意多字符。
如果⽤户输⼊参数中包括这些通配符,就会出现结果不正确的问题。
⽐如说:
WHERE T2.name like (%+ @Param + %)
如果⽤户输⼊下划线,他期待的结果应该是name字段值含有下划线的记录,但是结果是所有记录都会被查询出来。输⼊百分号也是如此。为此,在将⽤户输⼊的内容作为参数值传⼊之前,必须进⾏通配符的转义处理(英⽂叫做Escape),也就是说,如果⽤户输⼊的查询条件中含有通配符,必须将这些字符作为数据⽽不是通配符来对待。
在SQL Server的查询语句中,将通配符转义为普通数据的⽅法是⽤⽅括号括起来。
⽐如说,如果想要查询带有下划线的字段,正确的写法是:
WHERE T2.name like (%+ [_] + %)
同样,如果想要查询带有百分号的字段,正确的写法是:
WHERE T2.name like (%+ [%] + %)
所以,即使使⽤参数化查询,也必须在将⽤户输⼊的内容当作参数值传⼊SqlCommand.Parameters之前,先进⾏下⾯的处理:
s = s.Replace("%", "[%]");
s = s.Replace("_", "[_]");
四. ⽅括号问题
如果你⾜够细⼼,可能发现了还有⼀个⽅括号问题。
既然⽅括号是⽤来界定数据内容的,那么如果⽤户输⼊的查询参数本⾝就包括⽅括号时,会出现什么结果呢?
根据⽤户的期望,如果输⼊⼀个⽅括号,查询结果中应该只包括那些字段值中含有⽅括号的记录。
但是实验结果表明,如果是没有配成对的单个左⽅括号,查询时这个左⽅括号会被忽略。
也就是说,下⾯这个语句:
WHERE T2.name like (%+ [ + %)
等价于下⾯这个语句:
WHERE T2.name like (%+ + %)
这将导致查询结果中包含表中的全部记录,就像没有任何过滤条件⼀样。
为此,如果⽤户输⼊的查询条件中含有左⽅括号的话,还必须对左⽅括号进⾏转义:
s = s.Replace("[", "[[]");
注:右⽅括号没有这个问题。
五. 其他注意事项
按照微软的建议,凡是有可能导致问题的输⼊,可以在UI部分就进⾏检查并拒掉。
这些可疑输⼊包括:
分号(;):多个查询语句之间的分隔符,注⼊攻击时的恶意查询语句往往就是第⼆个查询语句。
sql语句替换表中内容
单引号():字符串数据分隔符,这是最危险的,前⾯已经讨论了。
注释符(–或者/*,*/):有些数据库可以利⽤注释设置⼀些查询引擎的⾏为,⽐如如何利⽤索引等。
xp_:扩展存储过程的前缀,SQL注⼊攻击得⼿之后,攻击者往往会通过执⾏xp_cmdshell之类的扩展存储过程,获取系统信息,甚⾄控制、破坏系统。
六、结论
为了防⽌SQL注⼊,同时避免⽤户输⼊特殊字符时查询结果不准确的问题,应该做两件事:
(1)使⽤参数化查询。
(2)在使⽤⽤户输⼊的字符串数据设置查询参数值之前,⾸先调⽤下⾯的共通处理函数:
private static string ConvertSql(string sql)
{
//sql = sql.Replace("", ""); // ADO.NET已经做了,不要⾃⼰做
sql = sql.Replace("[", "[[]"); // 这句话⼀定要在下⾯两个语句之前,否则作为转义符的⽅括号会被当作数据被再次处理
sql = sql.Replace("_", "[_]");
sql = sql.Replace("%", "[%]");
return sql;
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。