预编译是怎么防御sql注入的
SQL 注入成功需要两点:
- 用户输入被拼接进 SQL 字符串
- 数据库把用户输入当成“SQL 语法的一部分”去解析
SQL 注入的本质是“控制 Parser”,而 ? 让 Parser 根本看不到用户输入。
预编译
查询分为parser和excute两次通信,第一次通信就解析了语法树,第二次通信直接进行了参数化查询
预编译的安全性来自三件事的协同:
1️⃣ **数据库协议支持「结构与参数分离」
**2️⃣ **数据库内核支持「预解析 + 执行计划缓存」
**3️⃣ 客户端 API(JDBC)暴露了这个能力
PREPARE -> 发送 SQL 结构
EXECUTE -> 发送参数值
| 层级 | 负责什么 |
|---|---|
| 数据库协议 | 定义“结构包 / 参数包”分离 |
| 数据库 Parser | 把 **?** 识别为 PARAM 节点 |
| 执行引擎 | 只允许参数参与值比较 |
| JDBC / Driver | 正确使用协议 |
| ORM | 默认帮你走这条路 |
数据库在 prepare 阶段已经数过:
ps.setString(1, username);
ps.setInt(2, age);
就是告诉驱动:
把这个值,绑定到 AST 里的 PARAM(1)


一个实现了能防sql注入的预编译,一个没有实现,框架白写,在语法paser前就已经写进去了
sql注入就是在执行一段sql语句,本质与语言无关
宽字节注入
宽字节注入成立的前提是:
应用层用“转义”而不是“参数绑定”, (让用户输入进入 SQL Parser)
且数据库在解析 SQL 时会按多字节字符集重新解码字节流, 攻击目标是转义字符(\)本身
从而把转义字符吞进字符,导致语法边界失效。 ( 数据库会“重新按字符集解码”SQL 字节流 )

宽字节注入依赖“某些多字节编码允许反斜杠成为字符的一部分”
审计小技巧
GBK / Big5 符合这个条件
UTF-8 不符合
查看数据库字符集连接文件,看它是否是utf-8
HQL注入

Hibernate是一种orm框架,我们的输入在进入之后作为HQL的一部分,渲染成sql语句,然后再进入数据库层进行查询,但就是因为这么一手渲染,防住了很多攻击,因为我们构造的语句需要在渲染成sql语句之后还能造成注入攻击才行
将数据库tables映射为相关的类,通过此类进行数据查询,使用的是HQL自己的语言
1 | String parameter = req.getParameter("name"); |
为啥会产生sql注入
开发者误用
$sql = “SELECT * FROM user WHERE name = ‘$name’”;
$stmt = $conn->prepare($sql);
在prepare之前,$name参数就已经拼接进去了,后边纯鸟用
无法预编译的输入点
like
在数据库查询中, like后边加 “%”.$username.”%” 这个百分号表示匹配,匹配包含里边的字符串
参数绑定只做一件事:
把一个“值”,安全地放到 SQL 已经确定的结构里
然而like语句 “模糊语义”不属于参数绑定能自动完成的事情。 数据库并不知道你要模糊到哪里
参数绑定是写死进去的直接参数绑定
比方说
1 | like a 也就是=a |
正确且安全的只有这个
1 | LIKE ? |
由于mybaits框架面对这个的话,需要开发者手动去进行预编译,手动预编译就可能存在手残没写好的情况
预编译没正确使用啊,过滤有问题啊之类的
1 | $stmt = $conn->prepare("select * from fraction where name like '%:username%'"); 不行 |
不能加‘的关键字
select $username$,password from $table$ where $username2$ = ‘$dada$order by $username3$ $desc$ limit $0$,1
总的来说就是
表名、列名、limit子句、order by[desc/asc]
产生sql注入的原因:
在语法结构里,预编译只能预编译值,不能搞这些结构
SELECT ‘username’ FROM ‘users’; – ❌ 错误
SELECT username FROM users; – ✅ 正确 加引号就会报错
例子
1 | $table = "users; DROP TABLE users;--" |
防御
1 | 三、如何安全处理这些结构 |