最好的方法必須是參數化查詢。然後,無論使用者輸入什麼,資料都會以值的形式傳送到資料庫。
快速線上搜尋顯示 PHP 中有一些可能性,這很棒!即使在本網站上 - https://php.dev.org.tw/manual/en/pdo.prepared-statements.php
這也說明了這種方法在安全性和效能方面都很好的原因。
SQL 注入是一種技術,攻擊者會利用應用程式程式碼中負責建構動態 SQL 查詢的缺陷。攻擊者可以獲得應用程式的特權部分的存取權,從資料庫中檢索所有資訊,篡改現有資料,甚至在資料庫主機上執行危險的系統級命令。當開發人員在 SQL 陳述式中串連或內插任意輸入時,就會發生此漏洞。
範例 #1 將結果集分割成頁面... 並建立超級使用者 (PostgreSQL)
在以下範例中,使用者輸入直接內插到 SQL 查詢中,允許攻擊者在資料庫中獲得超級使用者帳戶。
<?php
$offset = $_GET['offset']; // 注意,沒有輸入驗證!
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);
?>
0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; --
0;
是為了為原始查詢提供有效的偏移量並終止它。
注意:
常見的技術是強制 SQL 解析器使用
--
來忽略開發人員編寫的查詢其餘部分,這是 SQL 中的註解符號。
一種可行的獲取密碼的方式是規避您的搜尋結果頁面。攻擊者唯一需要做的就是查看 SQL 陳述式中是否有任何未正確處理的提交變數。這些篩選器通常可以在前面的表單中設定,以自訂 SELECT
陳述式中的 WHERE、ORDER BY、LIMIT
和 OFFSET
子句。如果您的資料庫支援 UNION
建構,攻擊者可以嘗試將整個查詢附加到原始查詢以列出任意表格中的密碼。強烈建議只儲存密碼的安全雜湊值,而不是密碼本身。
範例 #2 列出文章 ... 以及一些密碼 (任何資料庫伺服器)
<?php
$query = "SELECT id, name, inserted, size FROM products
WHERE size = '$size'";
$result = odbc_exec($conn, $query);
?>
SELECT
陳述式結合,以揭示所有密碼' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; --
UPDATE
和 INSERT
陳述式也容易受到此類攻擊。
範例 #3 從重設密碼 ... 到獲得更多權限 (任何資料庫伺服器)
<?php
$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
' or uid like'%admin%'
到 $uid 以變更管理員的密碼,或只是將 $pwd 設定為 hehehe', trusted=100, admin='yes
以獲得更多權限,則查詢將會被扭曲<?php
// $uid: ' or uid like '%admin%
$query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%';";
// $pwd: hehehe', trusted=100, admin='yes
$query = "UPDATE usertable SET pwd='hehehe', trusted=100, admin='yes' WHERE
...;";
?>
雖然很明顯,攻擊者必須至少對資料庫架構有一些瞭解才能成功進行攻擊,但獲得此資訊通常非常簡單。例如,程式碼可能是開源軟體的一部分,並且是公開可用的。此資訊也可能會被封閉原始碼洩露 - 即使它是編碼、混淆或編譯的 - 甚至透過顯示錯誤訊息的程式碼洩露。其他方法包括使用典型的表格和欄名稱。例如,使用 'users' 表格,欄名稱為 'id'、'username' 和 'password' 的登入表單。
範例 #4 攻擊資料庫主機作業系統 (MSSQL Server)
一個令人恐懼的範例,說明如何在某些資料庫主機上存取作業系統級命令。
<?php
$query = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);
?>
a%' exec master..xp_cmdshell 'net user test testpass /ADD' --
到 $prod,則 $query 將會是<?php
$query = "SELECT * FROM products
WHERE id LIKE '%a%'
exec master..xp_cmdshell 'net user test testpass /ADD' --%'";
$result = mssql_query($query);
?>
sa
身分執行,並且 MSSQLSERVER 服務以足夠的權限執行,則攻擊者現在將擁有一個可用於存取此機器的帳戶。
注意:
上面的某些範例與特定的資料庫伺服器相關,但這並不表示針對其他產品不可能進行類似的攻擊。您的資料庫伺服器可能以另一種方式同樣容易受到攻擊。
圖片由 » xkcd 提供
避免 SQL 注入的建議方法是透過準備好的陳述式繫結所有資料。使用參數化查詢不足以完全避免 SQL 注入,但它是向 SQL 陳述式提供輸入最簡單且最安全的方式。 WHERE
、SET
和 VALUES
子句中的所有動態資料常值都必須替換為佔位符。實際資料將在執行期間繫結,並與 SQL 命令分開傳送。
參數繫結只能用於資料。SQL 查詢的其他動態部分必須針對已知允許的值清單進行篩選。
範例 #5 透過使用 PDO 準備好的陳述式來避免 SQL 注入
<?php
// 動態 SQL 部分會針對預期值進行驗證
$sortingOrder = $_GET['sortingOrder'] === 'DESC' ? 'DESC' : 'ASC';
$productId = $_GET['productId'];
// SQL 使用預留位置進行準備
$stmt = $pdo->prepare("SELECT * FROM products WHERE id LIKE ? ORDER BY price {$sortingOrder}");
// 值使用 LIKE 的萬用字元提供
$stmt->execute(["%{$productId}%"]);
?>
預處理語句由 PDO、MySQLi 和其他資料庫函式庫提供。
SQL 注入攻擊主要基於利用未考慮安全性的程式碼。永遠不要信任任何輸入,特別是來自客戶端的輸入,即使它來自選取方塊、隱藏的輸入欄位或 Cookie。第一個範例顯示,如此簡單的查詢可能會導致災難。
深度防禦策略涉及多種良好的程式碼編寫實務
除此之外,如果資料庫本身支援記錄,您也可以從在您的腳本中或由資料庫本身記錄查詢中獲益。顯然,記錄無法防止任何有害的嘗試,但它可以幫助追蹤哪個應用程式被繞過了。記錄本身沒有用,但它包含的資訊很有用。更詳細通常比更少更好。
最好的方法必須是參數化查詢。然後,無論使用者輸入什麼,資料都會以值的形式傳送到資料庫。
快速線上搜尋顯示 PHP 中有一些可能性,這很棒!即使在本網站上 - https://php.dev.org.tw/manual/en/pdo.prepared-statements.php
這也說明了這種方法在安全性和效能方面都很好的原因。