mysqli_prepare()更安全是因为它将SQL结构与数据彻底分离:先编译语句模板,再绑定参数,用户输入如' OR 1=1仅作字符串值处理,无法改变查询逻辑;正确用法是SQL中仅用?占位符,通过bind_param绑定变量,严禁拼接表名、列名等动态结构。

为什么 mysqli_prepare() 比直接拼接 SQL 更安全
因为 mysqli_prepare() 把 SQL 语句结构和数据彻底分离:语句模板先编译,参数后绑定,数据库引擎不会把传入的值当 SQL 代码执行。哪怕用户输入 ' OR '1'='1,它也只会被当作一个字符串字面量处理,不可能改变查询逻辑。
怎么用 mysqli_prepare() 写一个带 WHERE 条件的查询
关键不是“写对”,而是“不拼接”。常见错误是仍用 .$username. 拼进 SQL 字符串里——这等于白用预处理。
- 正确做法:SQL 中只留
?占位符,如"SELECT * FROM users WHERE username = ?" - 调用
mysqli_prepare($conn, $sql)得到 stmt 对象 - 用
mysqli_stmt_bind_param($stmt, 's', $username)绑定变量,'s'表示字符串类型 - 最后
mysqli_stmt_execute($stmt)执行,结果用mysqli_stmt_get_result()取
PDO::prepare() 的 bindParam 和 execute 顺序不能错
很多人卡在 $stmt->execute([$username]) 直接传数组,却忘了如果之前调用了 bindParam(),变量必须在 execute() 前保持有效。PHP 的变量作用域或引用传递稍有偏差,就会查不到数据,但又不报错。
- 推荐新手统一用
execute([$username])形式,避免引用陷阱 - 如果要用
bindParam(),确保绑定的变量在execute()时未被 unset 或重赋值 - 注意类型标识符:
's'(string)、'i'(integer)、'd'(double)、'b'(blob)必须匹配实际数据
MyBatis 里 #{} 和 ${} 的区别不是语法糖,是生死线
在 MyBatis XML 中,#{id} 走的是 JDBC PreparedStatement,等价于 mysqli_prepare();而 ${id} 是纯字符串替换,等同于手动拼接 SQL —— 这就是漏洞高发区。
-
ORDER BY ${column}、IN (${ids})、LIKE '%${keyword}%'都属于危险模式 - 替代方案:
ORDER BY必须用白名单校验字段名;IN改用<foreach></foreach>标签配合#{item};LIKE改用CONCAT('%', #{keyword}, '%') - 别信“我只在后台用”,只要参数来自 HTTP 请求(哪怕 POST),就存在注入可能
预处理本身不能自动修复业务逻辑缺陷,比如没做权限校验、没限制查询条数、没关闭调试模式导致报错泄露表结构——这些都会让攻击者更容易利用已存在的注入点。安全不是加个 prepare() 就结束的事。











