唯一可靠防SQL注入的方法是使用PDO::prepare()或mysqli_prepare()预处理语句,因其将SQL结构与数据彻底分离;禁用模拟预处理、动态标识符须白名单校验,addslashes()和mysql_real_escape_string()等函数已废弃且不安全。
和
是 PHP 防御 SQL 注入最可靠的方式,其他所谓“过滤函数”或“转义函数”在复杂场景下极易失效。
用
替代
老项目里常见
,但它只对单字节字符集有效,且无法防御宽字节注入、多语句注入等变种。现代 PHP(7.4+)已彻底移除该函数。
正确做法是统一使用
并开启
:
必须关闭模拟预处理(
),否则 PHP 会在客户端拼接 SQL,失去防护意义
占位符只能用于数据值,不能用于表名、列名、排序方向(
是非法的)
如果真要动态列名,只能白名单校验:
下必须用
,不是
很多开发者误以为
能替代预处理——它不能。该函数只是做字符串转义,仍可能被绕过(如 GBK 宽字节漏洞、JSON 字段内嵌 SQL 等)。
立即学习
“
PHP免费学习笔记(深入)
”;
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
正确写法:
的第一个参数是类型字符串:
(string)、
(integer)、
(double)、
(blob)
不要用
—— 这和拼接字符串没本质区别
注意:
不支持多语句(
),这是好事,反而堵住了一类攻击面
别在应用层“自己造轮子”过滤输入
OWASP Top 10 2025 明确指出,“输入验证”只是辅助手段,不能替代参数化查询。试图用正则过滤
、
、
、
等符号,既不全面也不可持续。
攻击者可绕过:用十六进制编码(
)、Unicode 同形字、注释变形(
)、或利用 JSON/NoSQL 特性发起非 SQL 类注入
业务字段本身就需要特殊字符(如用户昵称含单引号、地址含分号),粗暴过滤会破坏功能
真正该做的,是在输出到 HTML 时用
,在输出到 JS 上下文时用
,在输出到 URL 时用
—— 场景决定编码方式,不是统一“过滤”
为什么你写了
还被扫出 SQL 注入?
常见原因不是函数没用,而是调用姿势错了:
用了
或
执行带变量的字符串 —— 即使前面声明了
对象也没用
把用户输入拼进了 SQL 模板里:
—— 表名不可能参数化,必须白名单限制
在 ORM 中混用原生查询(如 Laravel 的
),绕过了 ORM 的参数绑定机制
错误地认为“用了框架就安全”,但框架的
、
等方法默认不绑定参数
参数化查询的防护边界非常清晰:只保数据值,不保语法结构。任何把用户输入塞进 SQL 字符串骨架里的操作,都在防线之外。
PDOmysqli_preparePDO::prepare()mysql_real_escape_string()mysql_real_escape_string()PDOPDO::ATTR_EMULATE_PREPARES = false$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ? AND status = ?");
$stmt->execute([$_GET['id'], 'active']);
PDO::ATTR_EMULATE_PREPARES = falseORDER BY ?in_array($_GET['sort'], ['name', 'created_at'], true)mysqlimysqli_prepare()mysqli_real_escape_string()mysqli_real_escape_string()$mysqli = new mysqli($host, $user, $pass, $db);
$stmt = $mysqli->prepare("INSERT INTO logs (ip, action) VALUES (?, ?)");
$stmt->bind_param("ss", $_SERVER['REMOTE_ADDR'], $_POST['action']);
$stmt->execute();
bind_param()sidbmysqli_query($mysqli, "INSERT ... '".mysqli_real_escape_string($mysqli, $x)."'")mysqli_prepare()INSERT; SELECT';--/*0x756e696f6e/!UNION/htmlspecialchars()json_encode()urlencode()prepare()PDO::query()mysqli_query()prepare"SELECT * FROM {$table} WHERE ..."DB::select(DB::raw("..."))whereRaw()orderByRaw()