必须停用 mysql_* 函数,改用 PDO 或 mysqli 预处理;所有用户输入须经占位符绑定,数字型字段强制类型转换;ORDER BY/LIMIT/表名等不可绑定位置需白名单校验;防范二次注入与逻辑漏洞。

为什么 mysql_query 拼接字符串一定会出事
因为用户输入直接参与 SQL 构建,哪怕只漏掉一个单引号或没转义反斜杠,就可能让 WHERE id = '$id' 变成 WHERE id = '1' OR '1'='1'。老式 mysql_* 函数早已废弃,且不支持预处理——它连基本的参数隔离都做不到。
常见错误现象:mysql_real_escape_string 被误认为“够用”,但它依赖连接字符集、对多字节编码绕过有历史漏洞,且无法防止数字型注入(比如 id=1 OR 1=1 直接绕过引号)。
- 必须停用所有
mysql_*系列函数 - 改用
PDO或mysqli,且只走预处理(prepare+execute)路径 - 不要在 SQL 字符串里拼接任何用户数据,包括
$_GET、$_POST、$_COOKIE
PDO 预处理到底该怎么写才安全
关键不是“用了 PDO”,而是是否真正用对了绑定方式。很多人写了 $pdo->prepare($sql),却还在 execute() 里传数组拼接字符串,或者用 bindParam 绑定变量但类型没设对。
正确做法是:SQL 模板中所有动态值都用占位符(:name 或 ?),执行时只传原始值,由驱动层完成类型适配和转义。
立即学习“PHP免费学习笔记(深入)”;
- 命名占位符更易读:
SELECT * FROM users WHERE status = :status AND age > :min_age - 执行时用
execute([':status' => $_POST['status'], ':min_age' => (int)$_POST['age']]),强制类型转换比依赖绑定类型更可靠 - 避免
bindValue($param, $value, PDO::PARAM_STR)这类手动指定——除非你明确需要字符串截断或空值处理 - 数字型字段务必先
(int)或filter_var($x, FILTER_VALIDATE_INT),再进绑定,防止恶意字符串触发隐式类型转换漏洞
哪些场景容易漏掉预处理
开发者常以为“只有 WHERE 条件才要防”,结果在 ORDER BY、LIMIT、表名、字段名这些地方硬拼字符串,而它们根本不能用参数绑定。
例如:ORDER BY $_GET['sort'] 或 LIMIT $_GET['offset'], $_GET['limit'] —— 这些位置不接受占位符,必须白名单校验。
-
ORDER BY字段:只允许从固定数组['id', 'name', 'created_at']中取值,用in_array($_GET['sort'], $allowed, true)判断 -
LIMIT参数:必须是整数,且建议加范围限制,如$offset = max(0, min(10000, (int)$_GET['offset'])) - 表名/字段名动态拼接:绝不用用户输入,改用配置映射,比如
$table_map = ['user' => 'users', 'post' => 'articles'] - LIKE 模糊查询:用
WHERE name LIKE CONCAT('%', :keyword, '%'),而不是"%{$_GET['k']}%"
还有哪些“看似安全”实则危险的操作
很多团队加了 WAF 或用 ORM 就以为万事大吉,但 ORM 的 whereRaw、DB::statement、WAF 的规则盲区,照样会放行构造精巧的 payload。
另一个高频坑是“二次注入”:数据入库时用了预处理,但读出来后又拼进新 SQL——入库安全 ≠ 使用安全。
- 永远不要信任数据库里的内容,
SELECT content FROM logs查出来的字段,如果再用于INSERT INTO reports的 VALUES,仍需重新绑定 - 使用
PDO::ATTR_EMULATE_PREPARES => false(MySQL 默认开启模拟预处理),否则某些版本下仍可能绕过 - 错误信息别暴露 SQL 结构,
display_errors = Off,用日志记录而非页面输出 - 权限最小化:Web 连接数据库的账号只给
SELECT/INSERT/UPDATE,禁用DROP/CREATE/LOAD_FILE
最易被忽略的一点:预处理只能防数据注入,防不了逻辑漏洞。比如用户能通过修改 user_id=123 访问他人数据,那跟 SQL 注入无关,但危害一样大——这类问题得靠权限模型和上下文校验,不是加个 prepare 就能解决的。











