在 PHP 中防止 SQL 注入

Subodh Poudel 2023年1月30日
  1. 使用预处理语句和 PDO 防止 PHP 中的 SQL 注入
  2. 在 PHP 中使用参数化查询的 prepared 语句来防止 SQL 注入
  3. PDO::ATTR_EMULATE_PREPARES 属性设置为 false 以防止 SQL 注入
在 PHP 中防止 SQL 注入

我们将介绍一种使用准备好的语句和 PHP 数据对象(PDO)在 PHP 中防止 SQL 注入的方法。我们使用 PDO 通过这种方法建立数据库通信。此方法将数据和查询分别发送到数据库服务器,从而防止了数据和服务器的混合。

我们将介绍一种使用准备好的语句和参数化查询来防止 PHP 在 SQL 中注入的方法。我们使用 mysqli 以这种方法建立数据库通信。该方法具有与第一种方法类似的工作机制。形成对比的只是我们用来防止 SQL 注入的 mysqli 函数。

我们将向你展示一个示例,该示例通过将准备好的语句的仿真设置为 false,来说明如何在 PHP 中使用 PDO 时避免 SQL 注入。

使用预处理语句和 PDO 防止 PHP 中的 SQL 注入

我们可以将预处理语句与 PDO 一起使用,以防止在 PHP 中进行 SQL 注入。当将查询和数据混合发送到数据库时,将发生 SQL 注入。在这种方法中,我们没有在 SQL 语句中指定数据的确切值。我们改用占位符。因此,将参数化的 SQL 语句作为第一个请求发送到服务器。我们使用 prepare() 函数来实现这一点。我们将第二个请求中参数的确切值绑定到数据库服务器。为此,我们使用 bindValue() 函数。这样,我们在第一个请求时发送程序,在第二个请求中发送数据。如果我们要求将数据与 SQL 代码一起使用,则用户可以更改程序并编写恶意代码。因此,它可以防止恶意 SQL 代码被注入到服务器中。

例如,表 users 包含以下字段和数据。

+----+-----------+----------+------------+
| id | firstname | lastname | dob        |
+----+-----------+----------+------------+
|  1 | bruce     |  rose    | 1998-02-13 |
|  2 | jeff      |  james   | 2000-03-30 |
+----+-----------+----------+------------+

它创建一个变量 $firstname,并为其分配名称 bruce。它创建变量 $sql 并在其上写了一个查询 SELECT * FROM users WHERE firstname =:fname;

不要为 firstname 写出确切的数据值。而是使用参数:fname。使用 $pdo 变量在查询变量上调用 prepare() 函数。将:fname 的值替换为变量 firstname。使用 execute() 函数执行该语句。如果凭据与数据库匹配,则使用 fetch() 函数检查结果。如果是这样,则显示所选行的 idfirstnamelastname

下面的示例演示了预准备语句的用法。它将 firstname 字段的值存储在变量中,以验证凭据是否匹配。如果该变量包含一些恶意代码,它将显示消息 Credentials do no match。这是因为 prepared() 函数仅接受参数化查询,而不允许使用确切的数据。$pdo 变量包含数据库连接的对象。

示例代码:

# php 7.*
<?php
$firstname = "bruce";
$sql = "SELECT * FROM users WHERE firstname =:fname ;";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(":fname", $firstname);
$stmt->execute();
if(!$result = $stmt->fetch(PDO::FETCH_OBJ)){
    echo "Credentials do no match";
} else {
    echo"Id: ".$result->id. " Name: ".$result->firstname." ".$result->lastname;
}
?>

输出:

Id: 1 Name: bruce rose

在 PHP 中使用参数化查询的 prepared 语句来防止 SQL 注入

我们可以将 prepared 语句与参数化查询一起使用,以防止在 PHP 中进行 SQL 注入。我们使用 mysqli() 函数的对象创建数据库连接。在这种方法中,我们使用问号符号 ? 作为数据的占位符。我们将 prepare() 函数用作上述方法。我们使用 bind_param() 函数将真实数据绑定到占位符中。该方法遵循与上述方法类似的工作机制。

例如,建立一个数据库连接,创建一个 mysqli() 函数的对象,并将其分配给变量 $conn。将名称 jeff 分配给变量 $firstname。创建变量 $sql 并编写查询 SELECT * FROM FROM where where first name = ?;。使用 $conn 变量调用查询变量上的 prepare() 函数。更换占位符 ? 与变量 $firstname 一起使用。使用 execute() 函数执行该语句。调用 get_result() 函数将结果存储在 $result 变量中。如果条件失败,请检查该行是否具有 num_rows 属性,并使用 exit() 函数返回消息 No Rows。调用 fetch_assoc() 方法,并将其存储在 while 循环中的 $row 变量中。显示所选行的 idfirstnamelastname

示例代码:

#php 7.x
<?php
$conn = new mysqli($host, $user, $pwd, $dbName);
$firstname = "jeff";
$sql = "SELECT * FROM users WHERE firstname = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $firstname);
$stmt->execute();
$result = $stmt->get_result();
if($result->num_rows === 0) exit('No rows');
    while($row = $result->fetch_assoc()) {
    echo"Id: ".$row['id']. " Name: ".$row['firstname']." ".$row['lastname'];
}

输出:

Id: 2 Name: jeff james

PDO::ATTR_EMULATE_PREPARES 属性设置为 false 以防止 SQL 注入

如果我们没有正确设置 PDO 属性,则在 PDO 中使用预处理语句可能不足以防止 SQL 注入。我们应该将 PDO::ATTR_EMULATE_PREPARES 属性设置为 false 以防止注入。如果将属性设置为 true,则 PDO 将仅模拟准备好的语句,而不使用它们。因此,系统将容易受到 SQL 注入的攻击。

例如,在变量 $pdo 中创建到数据库的 PDO 连接。使用该变量调用 setAttribute() 函数。将属性 PDO::ATTR_EMULATE_PREPARES 设置为 false。

代码示例:

#php 7.x
<?php
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
?>
作者: Subodh Poudel
Subodh Poudel avatar Subodh Poudel avatar

Subodh is a proactive software engineer, specialized in fintech industry and a writer who loves to express his software development learnings and set of skills through blogs and articles.

LinkedIn