首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 防止 SQL 注入完整指南:預處理語句與參數化查詢

     編輯:PHP 相關知識

什麼是 SQL 注入?

當使用者輸入未經過濾直接拼接進 SQL 語句,攻擊者可注入惡意程式碼竊取、修改或刪除資料。例如:

$unsafe = $_POST['user_input'];
mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe')");

若輸入 value'); DROP TABLE table;--,實際執行的 SQL 變為:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

核心防禦:預處理語句

預處理語句將 SQL 邏輯與資料分離,參數視為純文字,徹底杜絕注入。PHP 提供兩種主流實作:

1. 使用 PDO(推薦)

<?php
// 建立連線並關鍵:關閉模擬預處理,使用真正的預處理
$pdo = new PDO('mysql:host=127.0.0.1;dbname=dbtest;charset=utf8', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// 查詢範例
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute(['name' => $name]);
foreach ($stmt as $row) {
    // 處理資料
}

// 新增範例
$stmt = $pdo->prepare('INSERT INTO `table` (`column`) VALUES (:col)');
$stmt->execute(['col' => $unsafeValue]);

2. 使用 mysqli

<?php
$mysqli = new mysqli('127.0.0.1', 'user', 'pass', 'dbtest');
$stmt = $mysqli->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name); // 's' 代表字串
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
    // 處理資料
}

為什麼要關閉 PDO 模擬預處理?

預設 PDO 會在 PHP 端模擬預處理,實際仍將參數拼接進 SQL 字串再送給 MySQL,若字元集設定不當仍有風險。設定 ATTR_EMULATE_PREPARES = false 強制使用 MySQL 原生預處理,確保參數在傳輸前不被 PHP 解析。

原理解析

  1. SQL 語句先送至資料庫解析、編譯、優化,生成執行計畫。
  2. 參數稍後以二進位協議傳送,資料庫直接套用至已編譯的計畫,不再重新解析 SQL 文字。
  3. 因此參數永遠不會被視為 SQL 指令,注入失效。

額外好處:同一連線重複執行相同預處理語句,資料庫只解析一次,效能提升。

常見誤區與補充

  • 舊版 mysql_* 函式已棄用,請改用 PDO 或 mysqli。
  • 資料表名、欄位名無法參數化,若需動態決定,請使用白名單驗證。
  • LIKE 查詢:參數值需自行加上 %,如 $stmt->execute(['keyword' => "%$kw%"])。
  • 錯誤處理:啟用 ERRMODE_EXCEPTION 並捕獲 PDOException,避免敏感資訊外洩。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved