[使用预处理语句和 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()
函数检查结果。如果是这样,则显示所选行的 id
、firstname
和 lastname
。
下面的示例演示了预准备语句的用法。它将 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
变量中。显示所选行的 id
,firstname
和 lastname
。
示例代码:
#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);
?>