使用PDO解决注入问题

<?php
$driver   = 'mysql';     // 数据库驱动
$host     = 'localhost'; // 主机地址
$dbname   = 'test_db';   // 数据库名
$port     = 3306;        // 服务端口号
$charset  = 'utf8mb4';   // 字符编码
$username = 'zhangsan';  // 账号
$password = 'pwd-1234';  // 密码

// PDO连接选项(重要说明:PHP模拟预处理实际上还是拼接SQL,注入问题依旧存在,把预处理工作交给MySQL服务端才可以彻底解决注入问题。)
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES $charset",
    PDO::ATTR_EMULATE_PREPARES => false, // 禁用PHP模拟预处理(把预处理工作交给MySQL服务端)
];

// 构造数据源名称(Data Source Name)字符串
$dsn = "$driver:";
$dsn .= "host=$host;";
$dsn .= "dbname=$dbname;";
$dsn .= "port=$port;";
$dsn .= "charset=$charset;";

// 创建PDO实例
try {
    $pdo = new PDO($dsn, $username, $password, $options);
} catch (PDOException $e) {
    exit('创建PDO实例失败:' . $e->getMessage());
}

// 构造SQL语句模板
$query = 'SELECT * FROM `prefix_user` WHERE `uid` = :uid LIMIT 1';

// 预处理待执行的SQL语句
try {
    $sth = $pdo->prepare($query);
} catch (PDOException $e) {
    exit('预处理待执行的SQL语句失败:' . $e->getMessage());
}

// 绑定参数
$params = ['uid' => 9527];
foreach ($params as $field => $value) {
    try {
        $sth->bindValue(":$field", $value);
    } catch (PDOException $e) {
        exit('绑定参数失败:' . $e->getMessage());
    }
}

// 执行SQL
$sth->execute();

// 获取查询结果集
$fetchAll = $sth->fetchAll();
$row = $fetchAll[0] ?? [];
if ($row) {
    echo "俺叫{$row['name']}{$row['gender']}),今年{$row['age']}岁。" . PHP_EOL; // 俺叫张三(男),今年18岁。
}


// 模糊查询示例
$sth = $pdo->prepare('SELECT * FROM `prefix_article` WHERE `title` LIKE :title LIMIT 1');
$sth->bindValue(':title', '%最好%'); // 查询title字段中包括“最好”的记录
$sth->execute();
$row = $sth->fetchAll()[0] ?? [];
if ($row) {
    echo "{$row['aid']} - {$row['title']}(点击数:{$row['click']})" . PHP_EOL; // 9527 - PHP是世界上最好の语言(点击数:1024)
}


/**
 * 修改/mysql/my.cnf文件,设置“general_log = ON”可看到使用PHP模拟预处理和MySQL预处理所执行的SQL是不同的。
 *
 * 使用PHP模拟预处理(PDO::ATTR_EMULATE_PREPARES => true)所执行的SQL
 * ----------------------------------------------------------------------------------------------------
 * Connect    zhangsan@localhost on test_db using Socket
 * Query      SET NAMES utf8mb4
 * Query      SELECT * FROM `prefix_user` WHERE `uid` = '9527' LIMIT 1
 * Quit
 *
 * 使用MySQL预处理(PDO::ATTR_EMULATE_PREPARES => false)所执行的SQL
 * ----------------------------------------------------------------------------------------------------
 * Connect    zhangsan@localhost on test_db using Socket
 * Query      SET NAMES utf8mb4
 * Prepare    SELECT * FROM `prefix_user` WHERE `uid` = ? LIMIT 1
 * Execute    SELECT * FROM `prefix_user` WHERE `uid` = '9527' LIMIT 1
 * Close stmt
 * Quit
 */

Copyright © 2024 码农人生. All Rights Reserved