最可靠的方式是用预处理语句分离SQL结构与用户数据,让数据库仅将参数视为值而非代码;必须配合PDO或MySQLi的预处理(禁用模拟预处理)、UTF8MB4字符集、输入类型验证、最小数据库权限及错误信息隐藏。

最可靠的方式是用预处理语句把 SQL 结构和用户数据彻底分开,让数据库自己保证输入只当数据用、不被当成代码执行。其他手段都是辅助,不能替代这一步。
优先用 PDO 或 MySQLi 的预处理语句
别拼字符串,改用占位符 + 绑定参数:
- PDO 推荐写法(注意关掉模拟预处理):`$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);`,再用 `prepare("SELECT * FROM users WHERE id = ?")` 和 `execute([$id])`
- MySQLi 对应写法:`$stmt = $mysqli->prepare("UPDATE logs SET msg = ? WHERE id = ?");`,然后 `bind_param("si", $msg, $id)`,类型标识必须准确("s" 字符串、"i" 整数等)
- 确保连接字符集设为 utf8mb4:`mysqli_set_charset($conn, 'utf8mb4')`,避免宽字节注入绕过
输入验证不能少,但得讲方法
验证不是为了“拦住所有坏字符”,而是确认数据符合业务预期:
- 数字类参数直接强转:`$id = (int)$_GET['id'];` 或 `$page = filter_var($_GET['p'], FILTER_VALIDATE_INT) ?: 1;`
- 邮箱、URL 等用内置过滤器:`filter_var($email, FILTER_VALIDATE_EMAIL)`
- 用户名、标签名这类字段,优先白名单正则:`/^[a-z0-9_]{3,20}$/i`,别依赖黑名单过滤关键字
数据库权限和错误处理要收紧
就算某处漏了防护,也要让攻击者拿不到太多东西:
立即学习“PHP免费学习笔记(深入)”;
- 给 PHP 应用配专用数据库账号,只开 SELECT/INSERT/UPDATE 必需权限,禁用 DROP、ALTER、GRANT、FILE 等高危操作
- 生产环境关掉错误显示:`ini_set('display_errors', 'Off');`,错误统一记到安全日志,不暴露表名、字段或 SQL 片段
- 动态用到表名或列名时(极少见),必须走白名单映射,绝不能由用户输入直接代入
别踩这些常见坑
有些做法看着像防护,实际无效甚至有害:
- 不用 `addslashes()` 或 `mysql_real_escape_string()` —— 宽字节、多编码场景下容易被绕过
- 不写自定义关键词黑名单 —— `union select` 可以写成 `UnIoN/**/SeLeCt`,注释、大小写、编码都能绕过
- 不继续用已废弃的 `mysql_*` 函数 —— PHP 7.0+ 已移除,且根本不支持预处理



















