在 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