什么时候在 PHP Mysqli 中使用准备好的语句? - 用户表单搜索输入与选择查询

Posted

技术标签:

【中文标题】什么时候在 PHP Mysqli 中使用准备好的语句? - 用户表单搜索输入与选择查询【英文标题】:When to use prepared statements in PHP Mysqli? - User form search input vs select queries 【发布时间】:2019-11-10 19:16:51 【问题描述】:

我试图了解何时应该在 php/mysqli 中使用准备好的语句。每个 php/mysqli 查询应该使用准备好的语句,还是只使用涉及用户输入的查询和实例……例如要求用户输入数据以在数据库中搜索的 html 表单?

我正在将旧的 php5/mysql 代码迁移到 php7/mysqli。我有许多查询 mysql 数据库的 php 文件。我想澄清一下,如果我需要为连接到 mysql db 的每个 php 文件使用准备好的语句......例如,通过“php require”引用的 php 文件并包含简单的 sql 选择语句来呈现图像和链接到 html页面?

<?php

//establish connection

$con = new mysqli('localhost','uid','pw','db');

//check connection

if ($con->connect_error) 
die("Connection failed: " . $con->connect_error);  


//search variable that stores user input

$search = "%$_POST['search']%";

//prepare, bind and fetch

$stmt = $con->prepare("SELECT image, caption FROM `tblimages`
WHERE catid = 3 AND caption LIKE ? order by caption ASC");
$stmt->bind_param("s", $search);
$stmt->execute();
$stmt->bind_result($image,$caption);

while ($stmt->fetch()) 
echo "$image <br> $caption <br>";    


$stmt->close();

//close database connection

mysqli_close($con);

?>

上面的代码有效,是我第一次使用准备好的语句。它从表单中获取用户输入(输入搜索词的空白框 - POST)并搜索 db ...然后将结果呈现到 html 页面。这似乎是对准备好的语句的逻辑使用。但是......我有其他 php 文件,用户从表单中的下拉框中选择数据以呈现结果(用户不会将数据输入到上面的搜索框中)。我是否也为该实例使用准备好的语句?另外,我是否对通过“php require”引用的 php 文件使用准备好的语句,并包含简单的 sql select 语句来呈现图像和指向 html 页面的链接?我还没有找到使用准备好的语句来防止 sql 注入的具体实例的说明。欢迎任何澄清或参考。

【问题讨论】:

简短回答:是的。当它包含来自 php.ini 的变量时,请始终使用准备好的语句。因为在您的示例(下拉列表)中,您可以轻松地操纵该下拉列表并将他想要的任何内容发送到您的服务器。 如何运行脚本并不重要。重要的是要防止用户输入或任何未经适当清理的值直接放入 SQL 查询中。 最后:为了安全起见(r) - 总是使用准备好的语句。 我不使用准备好的语句的唯一情况是查询中根本没有变量。 相关:***.com/questions/22534183/… 【参考方案1】:

简答:始终使用准备好的语句。

长答案:

准备好的语句将您的数据与 SQL 命令分开。它们由PDO 或MySQLi 提供。他们最大的优势是,如果你的数据被当作数据处理,就不可能有 SQL 注入。另一个优点是您可以使用不同的数据集一遍又一遍地执行相同的查询,这可能对您的性能更好,并且通常会使您的代码更干净。

但是,有时您希望根据用户的选择或操作进行某种动态查询。您可能知道表名和列名不是数据,而是 SQL 查询的一部分,因此您不能将它们分开。然后,准备好的语句的替代方法是有一个可能值的白名单,并且只允许根据白名单验证用户输入。

您可能会问queryreal_querymulti_queryPDO::exec 有什么用处? 正如 PHP 手册显示的那样,当您只需要执行没有任何变量的常量查询或当您有一个无法准备的查询时,它们非常有用。例如

$mysqli->query('SELECT Name FROM City LIMIT 10');
$pdo->exec('DELETE FROM fruit');
$mysqli->multi_query('DELETE FROM fruit; DELETE FROM pets;');

如果您知道数据的类型和值怎么办?您还应该准备/绑定吗? 是的!养成将所有数据与 SQL 查询绑定的习惯。没有理由例外。在您的代码中跟踪这些异常要困难得多,并且始终确保您不会用一些未知的输入覆盖“安全”值。

如果您仍然不确定如何使用准备好的语句,或者您认为它们太复杂(它们不是),您可以查看https://phpdelusions.net 的精彩 PHP 教程

【讨论】:

【参考方案2】:

这就是 MySQLi 准备好的语句在 PHP 中的工作方式:

准备一个使用空值作为占位符的 SQL 查询(每个值都有一个问号)。 通过说明每个变量及其类型,将变量绑定到占位符。 执行查询。 允许的四种变量类型:

i - 整数

d - 双倍

s - 字符串

b - 斑点

正如其名称所暗示的,准备好的语句是一种准备 MySQL 调用的方法,而不存储变量。你告诉它变量最终会到达那里——只是还没有。展示它的最佳方式是通过示例。

$stmt = $mysqli->prepare("SELECT * FROM myTable WHERE name = ? AND age = ?");
$stmt->bind_param("si", $_POST['name'], $_POST['age']);
$stmt->execute();
//fetching result would go here, but will be covered later
$stmt->close();

如果您以前从未见过准备好的语句,这可能看起来有点奇怪。 基本上发生的事情是您正在为 SQL 语句创建一个模板。 在这种情况下,我们从 myTable 中选择所有内容,其中 name 和 age 相等?。问号只是值将去哪里的占位符。

bind_param() 方法用于将变量附加到准备好的模板中的虚拟值。 请注意变量之前的引号中有两个字母。 这告诉数据库变量类型。 s 指定 name 将是一个字符串值,而 i 强制 age 是一个整数。 这正是我没有在名称的问号周围添加引号的原因,就像我通常在 SQL 调用中为字符串添加引号一样。 你可能以为我只是忘记了,但事实是根本没有必要(事实上,如果你在 ? 周围加上引号,它实际上是行不通的,因为它将被视为字符串文字,而不是一个虚拟占位符。)。

当您调用 bind_param() 时,您已经告诉它它将是一个字符串文字,因此即使恶意用户尝试将 SQL 插入您的用户输入,它仍将被视为字符串。 $stmt->execute() 然后实际运行代码;最后一行只是关闭准备好的语句。我们将在 Select 部分介绍获取结果。

【讨论】:

以上是关于什么时候在 PHP Mysqli 中使用准备好的语句? - 用户表单搜索输入与选择查询的主要内容,如果未能解决你的问题,请参考以下文章

如何使用mysqli准备好的语句?

PHP mysqli - 从准备好的语句返回关联数组

运行准备好的语句 (MySQL/PHP)

如何将 MySQLi 准备好的语句与存储过程一起使用

bind_result 成数组 PHP mysqli 准备好的语句

mysqli_stmt::bind_result():绑定变量的数量与准备好的语句中的字段数量不匹配