日历归档 |
|
<< < 2024 - 11 > >> | Su | Mo | Tu | We | Th | Fr | Sa | | | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
|
|
|
About Me |
|
|
ZhangSichu |
|
Male |
|
32 |
|
ZhangSichu@gmail.com |
|
ZhangSichu@hotmail.com |
|
ZhangSichu.com |
|
weibo.com/zhangsichu |
|
|
|
个人推荐 |
|
|
|
|
分类归档 |
|
|
|
|
My Friends |
|
|
|
|
防止SQL注入攻击的注意事项[转载]
|
非常精彩的文章,讲述了防止SQL注入攻击的注意事项。 原文地址: http://gcdn.grapecity.com/cs/forums/thread/9436.aspx
-------------------------------------------------------------------------------------------------------
一. 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部分就进行检查并拒掉。 这些可疑输入包括: 分号(;):多个查询语句之间的分隔符,注入攻击时的恶意查询语句往往就是第二个查询语句。 单引号():字符串数据分隔符,这是最危险的,前面已经讨论了。 注释符(--或者/*,*/):有些数据库可以利用注释设置一些查询引擎的行为,比如如何利用索引等。 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; }
|
|
|
|
|
|