问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

预编译是如何阻止SQL注入的?

创作时间:
作者:
@小白创作中心

预编译是如何阻止SQL注入的?

引用
1
来源
1.
https://www.freebuf.com/articles/web/399935.html

SQL注入是Web安全领域常见的攻击手段之一,而预编译是防御SQL注入的有效方法。本文将详细介绍预编译的工作原理、其在防止SQL注入中的作用,以及在模糊查询等特定场景下的应用和局限性。

1. SQL注入原理

SQL注入是指攻击者通过在输入参数中插入恶意SQL语句,使这些语句被数据库服务器错误地解析和执行。例如,通过在id变量后插入or 1=1这样的条件,可以绕过身份验证,获得未授权数据的访问权。

SELECT * FROM user WHERE id = -1 or 1=1

由于or 1=1满足永真结果,上述SQL语句会执行输出user表中的全部内容。

2. SQL注入的防御方式

  1. 定制严格的白名单校验:加强对用户输入的验证,限制用户输入内容的大小和数据类型。
  2. 设置数据库权限:遵循最小化原则,根据程序要求为特定的表设置特定的权限。
  3. 限制目录权限:WEB目录应至少遵循“可写目录不可执行,可执行目录不可写”的原则。
  4. 预编译:使用参数而不是将用户输入变量嵌入到SQL语句中,可以杜绝大部分的SQL注入式攻击。

3. 预编译的原理

预编译的核心思想是用占位符替代参数值,预先建立语法树。恶意语句不参与语法树的建立,所以不影响SQL语法,也就无法造成恶意注入。

以MySQL为例,数据库在执行SQL语句时,需要经历7个步骤:

  1. 词法分析:将SQL语句分解成一个个token(关键字、标识符、运算符),然后对token进行分类和解析,生成相应的数据结构。
  2. 语法分析:根据SQL语法检测规则检查语法是否正确,并生成语法树。
  3. 语义分析:遍历语法树,确定表和列等信息,同时检查语义的正确性。
  4. 优化处理:使用优化器对SQL语句进行处理和优化,比如执行计划、索引等。
  5. 执行计划:使用执行计划生成器生成SQL语句的执行计划,比如数据的访问方式,索引的使用方式等。
  6. 引擎执行:将执行计划发送给相应的数据库引擎进行处理,执行计划被翻译成底层的操作指令,执行数据扫描、索引查找、排序、分组等操作。
  7. 返回数据:将执行结果返回给客户端,比如查询结果集或操作结果。

预编译将SQL语句模板化,用占位符替代值(参数化绑定)并存储在数据库中,以便在需要时再传入值执行,省掉了重复建立语法树的时间,实现快速执行。

以MySQL为例,利用mysqli的预编译功能编写的核心PHP语句为:

//定义需要预编译的SQL语句,从外界传递的参数(输入)用占位符?表示
$sql= "SELECT FROM security.users WHERE id= ? LIMIT 0,1";
//创建预处理对象
$mysqli_stmt = $mysqli->prepare($sql);
//绑定参数
$mysqli_stmt->bind_param('i', $id);
//绑定结果集
$mysqli_stmt->bind_result($id, $username, $password);
//执行
$mysqli_stmt->execute();

预编译防止SQL注入的原理是:正常情况下,用户输入的参数会直接参与SQL语法的编译,而预编译则是先构建语法树,确定SQL语法结构以后,再拼接用户的参数。注入的恶意SQL语句只会被视为参数,参与不了SQL语句的语法树构建,也就无法改变其语法结构,也就无法达到编译恶意语句的目的。

4. 预编译的局限性

预编译的机制是先编译,再传值,用户传递的参数无法改变SQL语法结构,从根本上解决了SQL注入的问题。但并不是所有参数都可以使用预编译。例如动态表名和列名的场景,在生成语法树的过程中,预处理器在进一步检查解析后的语法树时,会检查数据表和数据列是否存在,因此数据表和数据列不能被占位符?所替代。

5. 模糊查询预编译

模糊查询本身并不支持预编译,占位符 ? 不适用于模糊查询中的通配符 %。占位符只能用于替换具体的值,而不能用于替换SQL语句中的其他结构,如通配符或标识符。解决方案是修改语句如下:

select id,name,age from people where address LIKE
concat('%',?,'%') order by id desc;
select id,name,age from people where address LIKE
concat('%',#{key,jdbcType=VARCHAR},'%') order by id desc;

在MyBatis场景下,$#的区别:

  • ${}:表示拼接sql串,将接收到参数的内容不加任何修饰拼接在sql中,可能引发sql注入。
  • #{ }:是预编译处理,MyBatis在处理#{ }时,它会将sql中的#{ }替换为?,然后调用PreparedStatement的set方法来赋值,传入字符串后,会在值两边加上单引号,使用占位符的方式提高效率,可以防止sql注入。因此最好使用#{ }方式。

6. 总结

预编译是利用占位符替代参数值,预先建立语法树的过程。注入的恶意SQL语句只会被视为参数,参与不了SQL语句的语法树构建,也就无法改变其语法结构,也就无法达到编译恶意语句的目的。然而,预编译有一些局限性,比如,模糊查询由于存在% _使得参数值是不确定的,需要修改预编译语句。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号