SQL注入解决思路(C#示例)

最近在编程中遇到一个SQL注入防护的问题。在这里顺便把SQL注入小结一下。以MYSQL(5.1)为例。

一、常规的SQL语句

观察SQL语句

SELECT * FROM [tableName] WHERE col1='value1'

这个语句由3个部分组成,SELECT子句筛选得到结果,FROM子句指定了筛选的范围,WHERE子句指定了条件。当在其中进行如下置换:
SELECT * FROM [tableName] WHERE 1
之后,选出了[tableName]表中的全部内容。分析其语法不难发现,WHERE将其后的真值表达式求值,然后作为筛选的条件。


二、WHERE子句的理解

现在(在MYSQL5.1)中测试以下语句
SELECT * FROM [tableName] WHERE false or 1=‘1’ and '1'='1' and "1"="1"
同样得到了[tableName]表种的全部内容。
这个测试说明了两点:
1、WHERE对后面的表达式进行真值计算
2、不带符号、‘与"都可以通过真值测试,可能不是个别特殊符号的问题


三、WHERE子句在编程中的使用

通常是采用字符串拼接的方式來构建WHERE子句。一般有:
SELECT * + FROM [tableName] + WHERE col1=' + value1 + ' 
其中的value1常常是由输入所得。然而当WHERE子句具备(二)中的某种特征时就可能存在意外了。如果只是按照拼接來构建WHERE子句的话,那么WHERE子句可能会演化成这样的形式:
SELECT * FROM [tableName] WHERE col1='badvalue' or 1='1'
也就是说,用户熟悉SQL语句并精心构造了一个包含SQL特殊字符的输入{badvalue‘ or 1=‘1},这时候会返回整张表。
当程序员采用"工作时,类似也有:
SELECT * FROM [tableName] WHERE col1="badvalue" or 1="1"
中的{badvalue" or 1="1}。
总之,一旦程序员采用直接将用户输入拼接进SQL语句时就存在偷换WHERE真值的风险。像这样,用户通过简单地改变终端输入而改变了应用逻辑的情况,叫做注入。


四、更加奔放的情况

观察上面大括号里面的内容,在MYSQL(5.1)中还存在这样的情况:
SELECT * FROM [tableName] WHERE col1='badvalue' ; select * from [another] where 1='1'
也就是说,填入{badvalue‘ ; select * from [another] where 1=‘1}。完全可以把填入括号中的select换成drop、update等等。起作用的前提是程序员不作任何处理地将用户输入接入了SQL查询语句中。
可以做一个简单的结论,SQL特殊字符在概念上是嵌套的,然而由于在处理中使用了直接拼接的方式,使得用户参与了SQL语句的编写,输入特殊字符让SQL截短解析,最终导致意料以外的后果。


五、解决问题的现状

完全没有必要搞得高深莫测。但是解决的方法通常是会引起争论的。
我用的最久的语言是C#。因为某种二缺的原因,我第一门认真学习的语言是C#。在这里给出一个C#的解决方法吧。


六、解决方法构思与部署

主要是要隔离开用户输入对“构建”SQL语句的参与权。用户输入只能作为数据提供。只要满足这个原则,WHERE子句的组装还是比较清晰的。
我们可以尽量把非用户输入的部分写完整,然后把用户输入的特殊字符全部转义。这样,就有了示意的Where函数

/**
  * affirmNodePair 示例:
  *   {"or col1=","value1"}
  *   {"and col2>","value2"}
* * /
public string Where (Dictionary<string,string> affirmNodePair)
{
	StringBuilder builder=new StringBuilder();
	// 预处理
	builder.Append(" where 1 ");
	
	foreach (var item in affirmNodePair) {
		builder.Append(item.Key);
		builder.Append("'");


		//过滤用户输入:将用户输入的特殊字符转义
		foreach (var c in item.Value) {
			switch (c) {
			case '\'':
				builder.Append(@"\");
				break;
			case '\"':
				builder.Append(@"\");
				break;
			case ';':
				builder.Append(@"\");
				break;
			default:
				break;
			}
			builder.Append(c);
		}
		builder.Append("'");


		builder.Append(" ");
	}
	return builder.ToString();
}



这样形成的Where子句类似于:
	Where 1 or col1='value1' and col2>'value2'
当填入{badvalue‘ or 1=‘1}后得到转义后的结果
	Where 1 or col1='badvalue\' or 1=\'1'
在我测试使用的MYSQL(5.1)中能够符合预期地报错。具体的部署要结合实际情况,限于工作条件,这里只提供了一个基础性思路。谢谢大家耐心看完。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。