应使用 mysqli_prepare() 和 bind_param() 实现预处理语句,将 SQL 结构与数据分离,彻底防止 SQL 注入;禁用模拟预处理、校验输入类型、白名单限制动态标识符,并关闭错误信息暴露。

用 mysqli_prepare() 和 bind_param() 替代拼接 SQL
直接把用户输入塞进 SQL 字符串里,比如 "SELECT * FROM user WHERE id = $_GET['id']",是 SQL 注入的典型入口。PHP 原生支持预处理语句,本质是把 SQL 结构和数据分开传输,数据库不会把参数当 SQL 语法解析。
实操要点:
-
mysqli_prepare()接收的是带问号占位符的 SQL,例如"SELECT * FROM user WHERE name = ? AND status = ?" -
bind_param()的第一个参数是类型字符串("s"表示 string,"i"表示 integer),后续参数必须是变量(不能是表达式或字面量) - 变量需提前声明并赋值,否则
bind_param()会静默失败
$stmt = $mysqli->prepare("SELECT * FROM user WHERE email = ? AND active = ?");
$stmt->bind_param("si", $email, $active);
$email = $_POST['email'] ?? '';
$active = 1;
$stmt->execute();
$result = $stmt->get_result();别信 mysql_real_escape_string() 或 addslashes()
这两个函数只是对特殊字符加反斜杠,依赖当前连接的字符集和 SQL 模式,绕过方式极多(比如宽字节注入、多字节编码截断)。PHP 7 已彻底移除 mysql_* 系列函数,addslashes() 更是完全不防注入——它连单引号都不一定逃得对,尤其在非 UTF-8 环境下。
常见错误现象:
立即学习“PHP免费学习笔记(深入)”;
- 用
addslashes($_GET['name'])拼接 SQL,输入O'Reilly看似正常,但配合 GBK 编码可能被解释为O%BF%27Reilly,导致单引号逃逸 - 以为
mysql_real_escape_string()能兜底,却忘了它必须在有效 MySQL 连接后调用,且不处理整数型参数(如id=1 OR 1=1直接穿透)
PDO 预处理同样可靠,但注意 PDO::ATTR_EMULATE_PREPARES
PDO 默认开启模拟预处理(emulate_prepares = true),此时 PHP 在客户端“假装”预处理,实际还是拼接 SQL + 转义,跟手写 addslashes() 几乎没区别。必须显式关闭才能走真正的服务端预处理。
正确初始化方式:
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
$stmt = $pdo->prepare("SELECT * FROM post WHERE slug = ?");
$stmt->execute([$_GET['slug']]);
注意:关闭模拟后,MySQL 服务端必须支持预处理(5.1+ 基本都支持),且某些复杂查询(如表名、列名动态化)无法用占位符,这类场景只能白名单校验 + preg_match() 严格过滤。
过滤和验证不是替代方案,而是补充手段
预处理解决的是“数据当代码执行”的问题,但业务逻辑层仍需校验输入合理性。比如用户 ID 应为正整数,邮箱需符合格式,分页 limit 参数不能是负数或超大值。
可操作建议:
- 整数参数优先用
(int)强转或filter_var($id, FILTER_VALIDATE_INT) - 邮箱用
filter_var($email, FILTER_VALIDATE_EMAIL),但别只靠它防注入——它不阻止admin@example.com' --这类攻击 - 动态表名/列名必须来自硬编码白名单:
$allowed_tables = ['user', 'post']; if (!in_array($table, $allowed_tables)) die('no');
最易被忽略的一点:哪怕用了预处理,如果错误信息直接输出 SQL 或数据库结构(比如 mysqli_error() 显示在页面上),攻击者仍能通过报错推断字段名、表名甚至数据内容。生产环境务必关闭 display_errors,用日志记录错误。











