prepare() 单独调用不防注入,因仅编译模板;pdo::attr_emulate_prepares 默认true需显式设false防绕过;order by等标识符不可用占位符,须白名单校验。

prepare() 单独调用不防注入,PDO::ATTR_EMULATE_PREPARES 默认是 true,ORDER BY 字段不能用占位符——这三个点没踩准,学得再熟也白搭。
为什么 prepare() 单独写就失效
prepare() 本身只是编译 SQL 模板的入口,不是防护开关。它只在配合绑定动作时才起作用。
- 错误写法:
$pdo->prepare("SELECT * FROM user WHERE name = '{$_POST['name']}'")—— 这里根本没占位符,prepare()返回个空壳对象,后续没execute()或bindValue(),整条语句还是字符串拼接 - 正确链条必须完整:
prepare()(带?或:name)→bindValue()/bindParam()/execute()(传数组) -
prepare()返回false或抛异常,说明 SQL 语法错,不是参数问题;得先修语句,再谈防注入
PDO::ATTR_EMULATE_PREPARES => false 必须显式设置
PHP 默认开启模拟预处理(true),即在 PHP 层自己做字符串替换,不交由 MySQL 服务端真正编译。宽字节环境(如未设 charset=utf8mb4)下极易被绕过。
- 连接时必须写全:
new PDO($dsn, $user, $pass, [PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]) - 验证是否生效:
var_dump($pdo->getAttribute(PDO::ATTR_EMULATE_PREPARES))输出false才算成功 - 不设这个,哪怕你用对了占位符和
execute(),攻击者仍可能通过编码漏洞注入
ORDER BY、表名、字段名这些地方死活不能用占位符
预处理机制只支持“值”(value)绑定,不支持“标识符”(identifier)绑定。SQL 语法结构部分(如排序字段、分组字段、表名、列名、LIMIT 后的 OFFSET)无法走占位符。
- 错误写法:
"SELECT * FROM {$table} ORDER BY {$sort_field}"—— 只拼一个变量,整条语句就不可信 - 正确做法:白名单校验 + 硬拼。例如:
$sort_field = in_array($_GET['sort'], ['id', 'name', 'created_at'], true) ? $_GET['sort'] : 'id'; - 常见被忽略点包括:
$_GET['sort']、$_POST['template'](用于include)、$_SERVER['HTTP_ACCEPT_LANGUAGE']、$_REQUEST['action']
命令执行和动态函数调用必须白名单兜底
escapeshellarg() 和 filter_var() 不是万能解药,它们解决不了“这个值是否被授权用于当前上下文”的问题。
- 危险场景举例:
call_user_func($_GET['callback'])、include $_POST['file'] . '.php'、shell_exec("convert {$input}.png ...") -
escapeshellarg()能防命令注入,但挡不住攻击者传入合法文件名后触发恶意逻辑(比如file=report实际加载了report.php里的后门) - 必须白名单限定完整值:
in_array($input, ['report.pdf', 'invoice.xlsx'], true),而不是只校验后缀或用正则过滤 - 白名单校验必须放在业务逻辑开始前,且不能被后续代码重赋值或绕过
真实防护从来不是靠“用了 prepare 就安全了”,而是每一步都卡住边界:SQL 结构部分白名单硬控,数据部分严格绑定,命令执行前彻底过滤,错误信息不泄露,危险函数直接禁用。最容易漏掉的是 PDO::ATTR_EMULATE_PREPARES 的显式关闭,以及把 $_GET['sort'] 当成普通参数随便拼进 SQL —— 这两个点一松,前面所有努力都归零。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











