产生背景
因为我使用sogou workflow 开发c++ http 后台,它没有提供预处理api,所以又思考sql注入,我跟AI聊了好久,重新认识到sql注入,我发现以前认知好多是错误的。
拼接字符串,如果直接传入用户数据,那么可能导致用户语句把用户字符串,变成含有其他语义的sql语句,你可以理解,这样子可以传入构建任何执行脚本,导致你的他可以访问你的数据或者可以修改你的数据
转义
这个转义基本说mysql 转义,这个是mysql 特有
这个是文档的介绍:
MySQL :: MySQL 8.0 Reference Manual :: 11.1.1 String Literals
为什么转义可以防止注入
因为mysql 解析时候检测含有\ 然后判断后面字符替换成对应字符串,这个可以我们c/c++ 写\n代换行符号,我现在突然明白以前为什么写c/c++语言 在字符串中\n \t \0代表,用来换行或者进行间隔。
mysql 在执行sql语句,估计编译sql 会检测这些符号,他不会遇到\' 中的单引号,认为是单引号,所以这里不是替换,不然\' 替换成单引号,不然就会截断sql
举例
用户可以查询名字获取用户信息,这个是最简单的sql
正常sql
select * from user_table where name = '?';
这里?是用户传入的名字
注入sql
用户名字是恶意构建: ‘or 1= 1--
select * from user_table where name = ''or 1= 1--'
等价的sql执行
select * from user_table
这个查询所有的数据
转义后的
用户名字是恶意构建: ‘or 1= 1--
select * from user_table where name = '\'or= 1--'
如果你用 markdown 支持sql语法,你会发现说明内容是字符串,不会截断字符串,但还是要mysql 客户端执行,你发现转义不会报错,说明\'
怎么转义
sogou workflow escape_string 转义的代码的
std::string MySQLUtil::escape_string(const std::string& str)
{
std::string res;
char escape;
size_t i;
for (i = 0; i < str.size(); i++)
{
switch (str[i])
{
case '\0':
escape = '0';
break;
case '\n':
escape = 'n';
break;
case '\r':
escape = 'r';
break;
case '\\':
escape = '\\';
break;
case '\'':
escape = '\'';
break;
case '\"':
escape = '\"';
break;
case '\032':
escape = 'Z';
break;
default:
res.push_back(str[i]);
continue;
}
res.push_back('\\');
res.push_back(escape);
}
return res;
}
这个把原来符号,转换成字符串,比喻一个单引号,转换一个反斜杠 和 单引号,我开始都没有明白,我就是\'就是单引号,这个是编译器会进行处理,这样子语法才不会报错。
前提
默认情况mysql开启转义,如果你要关闭这个转义,你要设置 SQL_MODE ,设置成 NO_BACKSLASH_ESCAPES,但这样子如果通过转义防止注入就是失效了
sql 转移
上面是c 语言特有的转移规则,只是mysql 支持这种c语言特有转移,貌似好多语言都支持这些转义。
' => ''
只有这种,所以其实用 sql 转义来保证防止sql 注入。
总结
- 转义基本能解决字符串中含有单引号和双引号的注入手段,多条语句拼接不在这篇讨论,所以你看到所有文章说,转义不能百分百解决,可能考虑编码和考虑是否关闭了Mysql转义的功能,但默认情况下就是开启的并用utf8编码,所以可以单条语句,并保证处理传入值是字符串一定被转义就没有问题了,这样子保证sql语句和值分开了,因为不会被截断
- 可以用sql 转义,所有字符串用''包含起来,然后包含值的内容,全部用' =>''转义,这个标准,所以sql 语法支持都支持这种,其实用这种方式更加好,但如果内容含有\可能就有问题,因为被数据库转义了,不过这个防注入没有关系,只是我存放JSON 字符串的时候发现中文就有\ 导致被数据转义,这个反斜杠就被吃掉,取出来就有问题了
- 遇到模糊的地方,应该多思考几次就会发现更多知识点,目前有AI,很多技术可以反复讨论
- 对了解决sql注入百分百肯定预处理了,因为数据库层面解决这个问题,因为明确区分脚本和数据,所以根本不会存在这个问题,SQL注入原因就是拼接字符串不能区分sql 和 数据。