SQL预编译

预编译有模拟预编译和真预编译两种

模拟预编译

模拟预编译本质上是将内容转换为字符串参数进行查询,对于引号则会进行转义,这种预编译本质上是不安全的,举一个例子:

宽字节注入

汉字是无法使用127以内的ascii码表示的,所以经过url编码后会生成类似于%df%27的字符,然后模拟预编译会尝试对%27也就是 ' 进行转义,让这个字符串变为%df&5c%27,这样子在数据库中%df&5c被识别为一个字符,引号就逃逸出来了,同时也避免了转义,可以实现sql注入

真预编译

真预编译的具体步骤为:先连接,然后准备语句,用?占位,接着用输入的语句替换?,然后执行,专业点的说法是:

  • 1.解析SQL;
  • 2.生成语法树(AST);
  • 3.执行;

真预编译本质上是为了提高数据库运行效率而出现的,防止SQL注入只是附加作用,不过为什么这样不会被宽字节注入呢,因为本质上我们的输入是被hex后才参与查询的,所以无法实现宽字节注入

无法预编译的场景

预编译也不是万能的,在有些场景是无法使用的,比如order by / group by这类的情况,它们后面的参数不能携带引号,如果携带引号,以order by为例,预编译后查询结果其实等同于order by NULL或者order by TRUE,还有列名,表名,limit等都会失效,这种情况我们的防护可以通过设置白名单,输入内容限制,构建映射表,前端传递引用数字。

注:order by 的注入可以通过rand函数来自己构建实现布尔盲注