专栏名称: 信安之路
只分享干货,不扯蛋不蹭热点,共同学习共同成长,一起踏上信息安全之路!
目录
相关文章推荐
智讯ZEX  ·  智讯护航台州律协发展论坛 | ... ·  2 天前  
智讯ZEX  ·  智讯护航台州律协发展论坛 | ... ·  2 天前  
财联社AI daily  ·  “支付宝崩了”,回应来了! ·  3 天前  
财联社AI daily  ·  “支付宝崩了”,回应来了! ·  3 天前  
元素和同位素地球化学  ·  《自然.材料》重大进展-美国院士团队首次揭示 ... ·  3 天前  
元素和同位素地球化学  ·  《自然.材料》重大进展-美国院士团队首次揭示 ... ·  3 天前  
吾爱破解论坛  ·  编写Markdown文章批量上传图片发布到论坛方法 ·  6 天前  
开源先锋  ·  1.9K star! ... ·  1 周前  
开源先锋  ·  1.9K star! ... ·  1 周前  
51好读  ›  专栏  ›  信安之路

预编译为什么可以防御 SQL 注入 ?

信安之路  · 公众号  · 互联网安全  · 2024-11-12 10:21

正文

预编译最初的目的是提高代码的复用性,因为有很多只有参数值不同的 SQL(完全相同的 SQL 会从缓存里查),比如:

select * from user where id='1'

select * from user where id='2'

这些 SQL 的语法树相同,但每次都要进行重复的编译,很浪费时间。

而预编译可以将 SQL 语句模板化,值的位置用占位符替代,这样数据库就会事先编译好 SQL 语法结构,等真正调用的时候,再传入值执行,省掉了重复建立语法树的时间。

select * from user where id={占位符}

通过抓包来看,SQL 语句先被预编译(Prepare Statement),参数值先用占位符替代。等执行(Execute Statement)的时候,再传入参数。

用户传入的参数不参与语法树的构建,就改不了 SQL 的语法结构,也就避免了注入。

以 MyBatis(半自动化的持久层框架)为例,#{id} 这种格式传参,会先把 SQL 传给数据库进行预编译,等调用的时候,再用参数替换掉占位符,然后执行。


         SELECT *
         FROM user
     WHERE id=#{id}

但有些 SQL 需要使用动态表名和列名,这种时候就不能使用预编译了,需要把 #{id} 换成 ${id},这样参数就会直接参与 SQL 编译,无法防止 SQL 注入,这时候就要手动过滤参数了。

提示:MyBatis 框架的预编译,是 JDBC 中的 PreparedStatement 类在起作用,它的对象包含了编译好的 SQL 语句。

PHP 中使用 MySQL 的预编译功能:

1)定义预编译的 SQL 语句,参数用占位符 ? 表示

$sql = "SELECT * FROM user WHERE id= ? ";

2)创建预处理对象

$mysqli_stmt = $mysqli->prepare($sql);

3)绑定参数

$mysqli_stmt->bind_param('i', $id);

4)绑定结果集

$mysqli_stmt->bind_result($username);

5)执行

$mysqli_stmt->execute();

预编译的局限性

预编译的机制是先编译,再传值,用户传递的参数无法改变 SQL 语法结构,从根本上解决了 SQL 注入的问题。

但并不是所有参数都可以使用预编译,比如动态表名和列名的场景,因为语义分析时,会解析语法树,检查表名和列名是否存在,所以表名和列名不能被占位符替代,也就没法使用预编译。

同理,排序场景的 ASC/DESC 也需要动态传参,不能使用预编译。