如何解决为PHP查询创建准备好的语句
这是我当前拥有的代码:
$resultSet = $link->query("SELECT kudos.sale_id as TheID,kudos.ident_id AS TheIdent from kudos,sales_gdpr where kudos.sale_id = $id AND sales_gdpr.id = kudos.sale_id");
if($stmt = $link -> prepare("SELECT COUNT(*) FROM kudos WHERE sale_id=? AND ident_id=?"))
{
$stmt -> bind_param("is",$id,$myIdent);
$stmt -> execute();
$stmt -> bind_result($count);
$stmt -> fetch();
$stmt -> close();
}
if ($count == 0) { // Not liked
echo "<a style='color:#FFFFFF' class='btn'> ? $resultSet->num_rows </a>";
} else { // Has liked
echo "<b style='color:#FFFFFF' class='btn'> ? $resultSet->num_rows </b>";
}
我已经将SELECT COUNT
做为准备好的陈述,但是我不知道如何将$resultSet
做为准备好的陈述。该代码现在可以正常运行,但是由于kudos.sale_id = $id
部分,我认为它对sql注入很脆弱。有人可以帮我吗?
解决方法
其中直接直接将外部值传递到SQL语句(如SELECT ... FROM ... WHERE id = $id
)中的代码对SQL injections开放。为了避免它们,应始终准备接收外部值的SQL语句以供执行。因此,应避免使用mysqli::query(因为它不允许进行任何准备)。
这里有两个关于“如何使$resultSet
作为准备好的语句” 的示例,描述了在MySQLi中准备SQL语句的两种适用方法,条件是{ {3}} (“ MySQL本机驱动程序”)。
方法1-如果安装了 mysqlnd 驱动程序:
此方法使用mysqlnd和mysqli_stmt::get_result。
为了安装 mysqlnd 驱动程序,条目“ extension = php_mysqli_mysqlnd.dll” (或类似名称)必须在PHP配置文件( “ php.ini” )和Web服务器,以及 mysql服务。
<?php
require 'connection.php';
/*
* Save the values,with which the database data will be filtered,into variables.
* These values will replace the parameter markers in the sql statement.
* They can come,for example,from a POST request of a submitted form.
*/
$saleId = 1;
/*
* The SQL statement to be prepared. Notice the so-called markers,* e.g. the "?" signs. They will be replaced later with the
* corresponding values when using mysqli_stmt::bind_param.
*
* @link http://php.net/manual/en/mysqli.prepare.php
*/
$sql = 'SELECT
k.sale_id AS saleId,k.ident_id AS identId
FROM
kudos AS k,sales_gdpr AS s
WHERE
k.sale_id = ? AND
s.id = k.sale_id';
/*
* Prepare the SQL statement for execution - ONLY ONCE.
*
* @link http://php.net/manual/en/mysqli.prepare.php
*/
$statement = $connection->prepare($sql);
/*
* Bind variables for the parameter markers (?) in the
* SQL statement that was passed to prepare(). The first
* argument of bind_param() is a string that contains one
* or more characters which specify the types for the
* corresponding bind variables.
*
* @link http://php.net/manual/en/mysqli-stmt.bind-param.php
*/
$statement->bind_param('i',$saleId);
/*
* Execute the prepared SQL statement.
* When executed any parameter markers which exist will
* automatically be replaced with the appropriate data.
*
* @link http://php.net/manual/en/mysqli-stmt.execute.php
*/
$statement->execute();
/*
* Get the result set from the prepared statement.
*
* NOTA BENE:
* Available only with mysqlnd ("MySQL Native Driver")! If this
* is not installed,then uncomment "extension=php_mysqli_mysqlnd.dll" in
* PHP config file (php.ini) and restart web server (I assume Apache) and
* mysql service. Or use the following functions instead:
* mysqli_stmt::store_result + mysqli_stmt::bind_result + mysqli_stmt::fetch.
*
* @link http://php.net/manual/en/mysqli-stmt.get-result.php
* @link https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
*/
$result = $statement->get_result();
/*
* Fetch all data at once and save it into an array.
*
* @link http://php.net/manual/en/mysqli-result.fetch-all.php
*/
$fetchedData = $result->fetch_all(MYSQLI_ASSOC);
/*
* ...or fetch and save one row at a time.
*
* @link https://secure.php.net/manual/en/mysqli-result.fetch-array.php
*/
// while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
// $fetchedData[] = $row;
// }
/*
* Free the memory associated with the result. You should
* always free your result when it is not needed anymore.
*
* @link http://php.net/manual/en/mysqli-result.free.php
*/
$result->close();
/*
* Close the prepared statement. It also deallocates the statement handle.
* If the statement has pending or unread results,it cancels them
* so that the next query can be executed.
*
* @link http://php.net/manual/en/mysqli-stmt.close.php
*/
$statement->close();
/*
* Close the previously opened database connection.
*
* @link http://php.net/manual/en/mysqli.close.php
*/
$connection->close();
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=yes" />
<meta charset="UTF-8" />
<!-- The above 3 meta tags must come first in the head -->
<title>Demo</title>
<style type="text/css">
body { padding: 10px; font-family: "Verdana",Arial,sans-serif; }
.result-set { border-collapse: separate; border: 1px solid #ccc; }
.result-set thead th { padding: 10px; background-color: #f3f3f3; }
.result-set tbody td { padding: 5px; }
</style>
</head>
<body>
<h3>
Result set
</h3>
<table class="result-set">
<thead>
<tr>
<th>Sale ID</th>
<th>Ident ID</th>
</tr>
</thead>
<tbody>
<?php
if ($fetchedData) {
foreach ($fetchedData as $item) {
$saleId = $item['saleId'];
$identId = $item['identId'];
?>
<tr class="result-set-record">
<td><?php echo $saleId; ?></td>
<td><?php echo $identId; ?></td>
</tr>
<?php
}
} else {
?>
<tr>
<td colspan="2">
No records found
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</body>
</html>
方法2-如果未/无法安装 mysqlnd 驱动程序:
此方法使用mysqli_result::fetch_all,mysqli_stmt::store_result和mysqli_stmt::bind_result。
<?php
require 'connection.php';
$saleId = 1;
$sql = 'SELECT
k.sale_id AS saleId,sales_gdpr AS s
WHERE
k.sale_id = ? AND
s.id = k.sale_id';
$statement = $connection->prepare($sql);
$statement->bind_param('i',$saleId);
$statement->execute();
/*
* Transfer the result set resulted from executing the prepared statement.
* E.g. store,e.g. buffer the result set into the (same) prepared statement.
*
* @link http://php.net/manual/en/mysqli-stmt.store-result.php
* @link https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
*/
$result = $statement->store_result();
/*
* Bind the result set columns to corresponding variables.
* E.g. these variables will hold the column values after fetching.
*
* @link http://php.net/manual/en/mysqli-stmt.bind-result.php
*/
$statement->bind_result($boundSaleId,$boundIdentId);
/*
* Fetch results from the result set (of the prepared statement) into the bound variables.
*
* @link http://php.net/manual/en/mysqli-stmt.fetch.php
*/
$fetchedData = [];
while ($statement->fetch()) {
$fetchedData[] = [
'saleId' => $boundSaleId,'identId' => $boundIdentId,];
}
/*
* Free the stored result memory associated with the statement,* which was allocated by mysqli_stmt::store_result.
*
* @link http://php.net/manual/en/mysqli-result.free.php
*/
$statement->free_result();
$statement->close();
$connection->close();
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,sans-serif; }
.result-set { border-collapse: separate; border: 1px solid #ccc; }
.result-set thead th { padding: 10px; background-color: #f3f3f3; }
.result-set tbody td { padding: 5px; }
</style>
</head>
<body>
<h3>
Result set
</h3>
<table class="result-set">
<thead>
<tr>
<th>Sale ID</th>
<th>Ident ID</th>
</tr>
</thead>
<tbody>
<?php
if ($fetchedData) {
foreach ($fetchedData as $item) {
$saleId = $item['saleId'];
$identId = $item['identId'];
?>
<tr class="result-set-record">
<td><?php echo $saleId; ?></td>
<td><?php echo $identId; ?></td>
</tr>
<?php
}
} else {
?>
<tr>
<td colspan="2">
No records found
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</body>
</html>
connection.php(包括在以上两个示例中):
有关详细信息,mysqli_stmt::fetch显示了如何报告mysqli中的错误。
<?php
/*
* This page contains the code for creating a mysqli connection instance.
*/
// Db configs.
define('HOST','localhost');
define('PORT',3306);
define('DATABASE','tests');
define('USERNAME','anyusername');
define('PASSWORD','anypassword');
/*
* Enable internal report functions. This enables the exception handling,* e.g. mysqli will not throw PHP warnings anymore,but mysqli exceptions
* (mysqli_sql_exception).
*
* MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
* MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
*
* @link http://php.net/manual/en/class.mysqli-driver.php
* @link http://php.net/manual/en/mysqli-driver.report-mode.php
* @link http://php.net/manual/en/mysqli.constants.php
*/
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
/*
* Create a new db connection.
*
* @see http://php.net/manual/en/mysqli.construct.php
*/
$connection = new mysqli(HOST,USERNAME,PASSWORD,DATABASE,PORT);
注释/建议:
我没有测试代码,但是它们应该可以工作。
我可以自由使用我的命名/编码约定。
我强烈建议您从MySQLi切换到this article。 PDO是非常好的PDO教程。
您尝试使用if($stmt = $link -> prepare(...)){...}
进行一些错误处理。但是,为了能够正确处理当前和将来的代码可能引起的任何类型的错误,我建议您通读Here以及该网站上与MySQLi相关的所有文章。
最后,尽可能避免从PHP代码中打印(例如,与echo
类似)HTML代码。尝试以干净的方式将PHP与HTML分开。因此,不要像这样:
if ($count == 0) {
echo "<a style='color:#FFFFFF' class='btn'> ? $resultSet->num_rows </a>";
} else {
echo "<b style='color:#FFFFFF' class='btn'> ? $resultSet->num_rows </b>";
}
您可以这样做:
<?php
//...
$count = ...;
$numRows = ...;
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,sans-serif; }
.num-rows-link { color: #fff; background-color: #f3f3f3; border: 1px solid #ccc; }
.num-rows-span { color: #fff; background-color: #f3f3f3; }
</style>
</head>
<body>
<?php
if ($count == 0) {
?>
<a class="btn num-rows-link">? <?php echo $numRows; ?></a>
<?php
} else {
?>
<span class="btn num-rows-span">? <?php echo $numRows; ?></span>
<?php
}
?>
</body>
</html>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。