每个语言都有自己的数据库框架或库,无论是哪种语言,哪种库,它们在数据库防注入方面使用的技术原理无外乎下面介绍的几种方法。
一、特殊字符转义处理
Mysql特殊字符指在mysql中具有特殊含义的字符,除了%
和_
是mysql特有的外,其他的和我们在C语句中接触的特殊字符一样。
特殊字符 | 转义字符 | 特殊意义 |
---|---|---|
\0 |
\\0 |
字符串结束符NUL |
' |
\' |
单引号 |
" |
\" |
双引号 |
\b |
\\b |
退格 |
\n |
\\n |
换行 |
\r |
\\r |
回车 |
\Z |
\\Z |
Control+Z |
\ |
\\ |
反斜杠 |
% |
\% |
百分号,模糊查询中匹配任意个任意字符 |
_ |
\_ |
下划线,模糊查询中匹配单个任意字符 |
mysql C API提供了mysql_real_escape_string
函数对转义字符进行处理,但根据实际经验,在使用该API时会产生诸多问题。
因此自己实现了一个类似的函数:
std::string MysqlEscapeString(const std::string &strSql) {
size_t iSrcSize = strSql.size();
std::string strDest;
for (size_t i = 0; i < iSrcSize; i++) {
char ch = strSql[i];
switch (ch)
{
case '\0':
strDest.append("\\0");
break;
case '\n':
strDest.append("\\n");
break;
case '\r':
strDest.append("\\r");
break;
case '\'':
strDest.append("\\'");
break;
case '"':
strDest.append("\\\"");
break;
case '\\':
strDest.append("\\\\");
break;
case '%':
strDest.append("\\%");
break;
case '_':
strDest.append("\\_");
break;
default:
strDest.append(1, ch);
break;
}
}
return strDest;
}
对于%
和_
这2个只在模糊查询条件中有特殊含义,而在普通字符串中没有其他含义的字符,需要根据字符使用在SQL语句中的具体位置来决定是否需要处理。如我们要查询memberName包含t_st的用户信息:
select * from member where memberName like '%t_st%;
如果不对_
进行转义处理则会查询出:
test
tast
tbst
t_st
二、Prepared SQL Statement(预处理语句)
SET @sql = "SELECT * FROM member WHERE memberName like ?";
SET @param = '%t_st%';
PREPARE stmt FROM @sql;
EXECUTE stmt using @param;
DEALLOCATE PREPARE stmt;
使用这种方法需要注意以下2点:
-
?
占位符不能用在字符串中,上面例子如果写成下面这样是错误的。
SET @sql = "SELECT * FROM member WHERE memberName like '%?%'";
SET @param = 't_st';
PREPARE stmt FROM @sql;
EXECUTE stmt using @param;
DEALLOCATE PREPARE stmt;
-
EXECUTE stmt USING @param;
中使用的@param变量,是会话级别的变量。该变量的作用域至整个连接,连接断开之后该变量才会释放。重复使用相同变量时要留意了。