
本文详解如何将存在 sql 注入风险的 mysqli 代码迁移到更安全、更现代的 pdo 方式,包括连接配置、预处理语句编写、参数绑定及错误处理,确保数据交互既健壮又符合最佳实践。
本文详解如何将存在 sql 注入风险的 mysqli 代码迁移到更安全、更现代的 pdo 方式,包括连接配置、预处理语句编写、参数绑定及错误处理,确保数据交互既健壮又符合最佳实践。
在 PHP 开发中,直接拼接用户输入到 SQL 查询字符串(如 WHERE id = '".$_POST["row_id"]."')极易引发 SQL 注入攻击,严重威胁应用安全。PDO(PHP Data Objects)作为 PHP 官方推荐的数据库访问抽象层,不仅支持多种数据库驱动,还内置对预处理语句(Prepared Statements)的一流支持,是防范 SQL 注入的首选方案。
以下是一个完整、可直接运行的 PDO 改写示例,替代原始脆弱的 mysqli 代码:
<?php header('Content-Type: application/json; charset=utf-8');
try {
// 创建 PDO 连接(启用异常模式,便于错误捕获)
$dsn = 'mysql:host=localhost;dbname=project;charset=utf8mb4';
$pdo = new PDO($dsn, 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 抛出异常而非静默失败
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 默认以关联数组返回结果
PDO::ATTR_EMULATE_PREPARES => false, // 禁用模拟预处理,强制使用真实预处理(提升安全性)
]);
// 验证并过滤输入(即使使用预处理,仍建议基础校验)
if (!isset($_POST['row_id']) || !is_numeric($_POST['row_id'])) {
throw new InvalidArgumentException('Invalid or missing row_id');
}
// 构建带命名占位符的预处理语句
$sql = "SELECT
id, username, usertype, division, mobnum, userstatus,
DATE(created_at) AS created_at
FROM users
WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':id', (int)$_POST['row_id'], PDO::PARAM_INT); // 显式类型绑定,更安全
$stmt->execute();
$row = $stmt->fetch();
echo json_encode($row ?: []); // 若无匹配记录,返回空对象
} catch (PDOException $e) {
// 生产环境请记录日志,避免暴露敏感信息给前端
error_log('PDO Error: ' . $e->getMessage());
http_response_code(500);
echo json_encode(['error' => 'Database query failed']);
} catch (InvalidArgumentException $e) {
http_response_code(400);
echo json_encode(['error' => $e->getMessage()]);
}
?>
关键要点说明:
✅ 连接配置严谨:charset=utf8mb4 防止字符集相关漏洞;PDO::ATTR_EMULATE_PREPARES => false 确保底层使用真正的预处理语句(尤其重要于 MySQL)。
✅ 参数绑定安全:使用 bindValue() 或 bindParam() 绑定变量,并显式指定数据类型(如 PDO::PARAM_INT),杜绝类型混淆风险。
✅ 错误处理完善:启用 PDO::ERRMODE_EXCEPTION,配合 try-catch 捕获所有数据库异常;对用户输入做前置校验,提升鲁棒性。
✅ 响应规范:设置正确的 Content-Type 头,确保前端能正确解析 JSON;空结果返回空对象而非 null,避免前端解析错误。
⚠️ 注意事项:
- 切勿在生产环境中暴露数据库凭证或错误详情,$e->getMessage() 仅用于调试;
- 若 id 字段为整型主键,强烈建议 (int) 强制转换后再绑定,双重保障;
- 所有用户输入(包括 GET/POST/COOKIE)都应视为不可信数据,无论是否使用 PDO,都需结合业务逻辑做必要验证与过滤。
通过以上方式重构,你的 fetch.php 不仅消除了 SQL 注入风险,还具备了更好的可维护性、可测试性和跨数据库兼容潜力。











