0

0

PDO预处理语句结合LIKE查询的最佳实践:如何正确绑定参数

心靈之曲

心靈之曲

发布时间:2025-11-24 10:19:26

|

974人浏览过

|

来源于php中文网

原创

PDO预处理语句结合LIKE查询的最佳实践:如何正确绑定参数

本文深入探讨了php pdo预处理语句在使用`like`操作时,绑定列名而非值的常见误区及其解决方案。核心在于,pdo预处理语句的占位符仅用于绑定值,不能用于动态插入表名或列名。正确的做法是,将列名(需严格净化)直接嵌入sql查询中,而仅通过占位符绑定`like`操作的搜索值,从而确保查询的安全性与有效性。

理解PDO预处理语句的原理

PHP数据对象(PDO)的预处理语句是防止SQL注入攻击的关键机制。其工作原理是将SQL语句的结构与实际数据分离。当执行prepare()方法时,数据库会解析SQL语句的结构,识别出占位符(如:name或?),但不会立即处理这些占位符中的值。只有在execute()方法执行时,通过bindValue()或bindParam()提供的值才会被安全地插入到预处理好的语句中。

关键在于,这些占位符设计用于绑定值(values),而不是SQL结构本身,例如表名、列名、操作符或关键字。尝试通过占位符绑定这些结构性元素会导致语法错误或,更常见的是,导致查询无法返回预期结果,因为数据库会将其解释为字符串字面量而非数据库标识符。

问题复现:错误的绑定方式

在使用LIKE操作进行模糊查询时,一个常见的错误是尝试绑定列名和搜索值。以下是一个错误的示例,它试图同时绑定WHERE子句中的列名和搜索词:

try {
    // 准备PDO语句,尝试绑定列名和搜索词
    $stmt = $readdb->prepare("SELECT * FROM athletes WHERE :search LIKE :term");

    // 绑定列名(错误用法)
    $stmt->bindValue(':search', $search);
    // 绑定搜索词
    $stmt->bindValue(':term', '%' . $term . '%');

    // 执行
    $stmt->execute();

    // ... 处理结果
} catch (PDOException $e) {
    // 错误处理
    echo "查询失败: " . $e->getMessage();
}

尽管这段代码不会抛出PDOException错误,但它通常不会返回任何结果。这是因为数据库将:search占位符绑定的值(例如,字符串'name')视为一个普通的字符串字面量,而不是数据库表中的列名。因此,查询变成了WHERE 'name' LIKE '%search_term%',这显然不是我们想要的效果,并且通常不会匹配任何记录。

解决方案:正确绑定LIKE查询

鉴于PDO占位符的限制,正确的做法是直接将列名嵌入到SQL查询字符串中,而只使用占位符来绑定动态的搜索值。然而,直接嵌入任何来自用户输入的动态内容都存在SQL注入风险,因此对列名进行严格的净化(Sanitization)是必不可少的。

以下是修正后的代码示例:

// 假设 $search 变量代表要查询的列名,例如 'name' 或 'description'
// 假设 $term 变量代表用户输入的搜索关键词

// 步骤1:严格净化列名(非常重要!)
$allowedColumns = ['name', 'sport', 'country']; // 定义允许查询的列名白名单
$searchColumn = 'name'; // 默认列名

if (isset($_GET['column']) && in_array($_GET['column'], $allowedColumns)) {
    $searchColumn = $_GET['column']; // 从用户输入获取并验证
}
// 确保 $searchColumn 变量现在是安全的,可以直接嵌入SQL

try {
    // 准备PDO语句,将净化后的列名直接嵌入,并绑定搜索词
    $stmt = $readdb->prepare("SELECT * FROM athletes WHERE {$searchColumn} LIKE :term");

    // 绑定搜索词
    $stmt->bindValue(':term', '%' . $term . '%');

    // 执行
    $stmt->execute();

    // 获取并处理结果
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    foreach ($results as $row) {
        // ... 处理每一行数据
        print_r($row);
    }

} catch (PDOException $e) {
    // 错误处理
    echo "查询失败: " . $e->getMessage();
}

在这个修正后的代码中:

造梦阁AI
造梦阁AI

AI小说推文一键成片,你的故事值得被看见

下载
  1. 我们首先定义了一个$allowedColumns白名单,包含了所有允许用户查询的列名。
  2. $searchColumn变量通过检查用户输入(例如$_GET['column'])并与白名单进行比对来确保其安全性。如果用户输入的列名不在白名单中,我们应该使用一个安全的默认值或拒绝请求。
  3. 净化后的$searchColumn被直接嵌入到SQL查询字符串中(WHERE {$searchColumn} LIKE :term)。
  4. LIKE操作符右侧的搜索值%...%则通过:term占位符进行安全绑定。

这种方法既解决了列名无法绑定的问题,又通过严格的列名净化机制维护了安全性。

安全考量:列名净化

如上所述,直接将变量嵌入SQL查询字符串中是潜在的SQL注入漏洞。当变量代表列名或表名时,由于它们不能被绑定,因此必须采取额外的安全措施。最可靠的方法是使用白名单验证

白名单验证步骤:

  1. 定义一个允许的列表: 创建一个数组,其中包含所有允许用户作为列名或表名使用的字符串。
  2. 验证用户输入: 检查用户提供的列名是否在您定义的白名单中。
  3. 处理无效输入: 如果用户提供的列名不在白名单中,则:
    • 使用一个安全的默认列名。
    • 抛出错误并拒绝请求。
    • 记录安全事件。

示例代码(列名净化函数):

/**
 * 净化列名,确保其在允许的白名单中。
 *
 * @param string $inputColumn 用户提供的列名。
 * @param array $allowedColumns 允许的列名白名单。
 * @param string|null $defaultColumn 如果输入无效,则使用的默认列名。
 * @return string 安全的列名。
 * @throws InvalidArgumentException 如果输入无效且没有提供有效的默认列名。
 */
function sanitizeColumnName(string $inputColumn, array $allowedColumns, ?string $defaultColumn = null): string
{
    if (in_array($inputColumn, $allowedColumns, true)) {
        return $inputColumn;
    }
    if ($defaultColumn !== null && in_array($defaultColumn, $allowedColumns, true)) {
        return $defaultColumn;
    }
    // 如果没有有效的默认列,或者默认列也不在白名单中,抛出异常
    throw new InvalidArgumentException("不允许的列名: '{$inputColumn}',且没有有效的默认列。");
}

// 使用示例
$userProvidedColumn = $_GET['column'] ?? ''; // 假设用户通过GET参数提供列名
$allowedAthleteColumns = ['id', 'name', 'sport', 'country', 'age'];

try {
    $safeColumn = sanitizeColumnName($userProvidedColumn, $allowedAthleteColumns, 'name');
    // 现在 $safeColumn 可以安全地用于SQL查询
    // 例如:
    // $stmt = $readdb->prepare("SELECT * FROM athletes WHERE {$safeColumn} LIKE :term");
    // $stmt->bindValue(':term', '%' . $term . '%');
    // $stmt->execute();
} catch (InvalidArgumentException $e) {
    echo "错误: " . $e->getMessage();
    // 终止脚本或采取其他错误处理措施
    exit;
}

通过这种方式,即使攻击者尝试注入恶意SQL代码作为列名,也只会被限制在预定义的白名单内,从而有效防止SQL注入。

总结

在PHP PDO预处理语句中,处理LIKE查询时,理解占位符的用途至关重要。占位符仅用于绑定,不能用于动态插入SQL结构(如列名、表名)。当需要动态指定查询列时,必须将列名直接嵌入SQL查询字符串中。为了防止SQL注入,对这些动态嵌入的列名进行严格的白名单验证和净化是不可或缺的安全实践。遵循这些原则,可以确保您的数据库查询既高效又安全。

相关专题

更多
php文件怎么打开
php文件怎么打开

打开php文件步骤:1、选择文本编辑器;2、在选择的文本编辑器中,创建一个新的文件,并将其保存为.php文件;3、在创建的PHP文件中,编写PHP代码;4、要在本地计算机上运行PHP文件,需要设置一个服务器环境;5、安装服务器环境后,需要将PHP文件放入服务器目录中;6、一旦将PHP文件放入服务器目录中,就可以通过浏览器来运行它。

2534

2023.09.01

php怎么取出数组的前几个元素
php怎么取出数组的前几个元素

取出php数组的前几个元素的方法有使用array_slice()函数、使用array_splice()函数、使用循环遍历、使用array_slice()函数和array_values()函数等。本专题为大家提供php数组相关的文章、下载、课程内容,供大家免费下载体验。

1604

2023.10.11

php反序列化失败怎么办
php反序列化失败怎么办

php反序列化失败的解决办法检查序列化数据。检查类定义、检查错误日志、更新PHP版本和应用安全措施等。本专题为大家提供php反序列化相关的文章、下载、课程内容,供大家免费下载体验。

1498

2023.10.11

php怎么连接mssql数据库
php怎么连接mssql数据库

连接方法:1、通过mssql_系列函数;2、通过sqlsrv_系列函数;3、通过odbc方式连接;4、通过PDO方式;5、通过COM方式连接。想了解php怎么连接mssql数据库的详细内容,可以访问下面的文章。

952

2023.10.23

php连接mssql数据库的方法
php连接mssql数据库的方法

php连接mssql数据库的方法有使用PHP的MSSQL扩展、使用PDO等。想了解更多php连接mssql数据库相关内容,可以阅读本专题下面的文章。

1416

2023.10.23

html怎么上传
html怎么上传

html通过使用HTML表单、JavaScript和PHP上传。更多关于html的问题详细请看本专题下面的文章。php中文网欢迎大家前来学习。

1234

2023.11.03

PHP出现乱码怎么解决
PHP出现乱码怎么解决

PHP出现乱码可以通过修改PHP文件头部的字符编码设置、检查PHP文件的编码格式、检查数据库连接设置和检查HTML页面的字符编码设置来解决。更多关于php乱码的问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1445

2023.11.09

php文件怎么在手机上打开
php文件怎么在手机上打开

php文件在手机上打开需要在手机上搭建一个能够运行php的服务器环境,并将php文件上传到服务器上。再在手机上的浏览器中输入服务器的IP地址或域名,加上php文件的路径,即可打开php文件并查看其内容。更多关于php相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1306

2023.11.13

Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

6

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 8.7万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 7万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号