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

PHP 防止 SQL 注入完整指南:伺服器設定、程式碼防護、現代最佳實務

     編輯:PHP 相關知識

PHP 防止 SQL 注入完整防護指南

SQL 注入是 Web 應用最嚴重的安全風險之一。本文從伺服器層面設定、程式碼層面防護、現代最佳實務三個維度完整說明。

一、伺服器與 PHP 設定 (php.ini)

設定項建議值說明
safe_modeOn限制危險函數、檔案存取(PHP 5.4+ 已移除)
safe_mode_gidOff允許同群組存取
open_basedir/var/www限制腳本可存取目錄
disable_functionsexec,shell_exec,system,passthru,popen,proc_open,phpinfo停用命令執行函數
expose_phpOff隱藏 PHP 版本資訊
register_globalsOff關閉自動全域變數(PHP 5.4+ 預設關閉)
magic_quotes_gpcOn自動跳脫輸入(PHP 5.4+ 已移除,不建議依賴)
display_errorsOff生產環境關閉錯誤顯示
log_errorsOn啟用錯誤記錄
error_log/var/log/php_error.log錯誤日誌路徑

二、程式碼層面防護原則

1. 初始化變數

<?php
// 錯誤:未初始化直接判斷
if ($admin) { include('admin.php'); }

// 正確:明確初始化
$admin = 0;
if ($_POST['user'] && $_POST['pass']) {
    // 驗證邏輯
    $admin = 1;
}
if ($admin) { include('admin.php'); }
?>

2. 輸入過濾與驗證

<?php
/** SQL 關鍵字檢查 */
function inject_check($str) {
    return preg_match('/select|insert|update|delete|\'|\\*|union/i', $str);
}

/** ID 參數驗證 */
function verify_id($id = null) {
    if (!$id) exit('缺少參數');
    if (inject_check($id)) exit('非法參數');
    if (!is_numeric($id)) exit('非法參數');
    return intval($id);
}

// 使用
$id = verify_id($_GET['id']);
$sql = "SELECT * FROM news WHERE id = $id";
?>

3. 字串跳脫處理

<?php
function str_check($str) {
    // magic_quotes_gpc 已棄用,建議統一手動處理
    if (!get_magic_quotes_gpc()) {
        $str = addslashes($str); // 單位元組字串
    }
    // SQL LIKE 萬用字元跳脫
    $str = str_replace('_', '\\_', $str);
    $str = str_replace('%', '\\%', $str);
    return $str;
}

function post_check($post) {
    $post = str_check($post);
    $post = nl2br($post);           // 換行轉 <br>
    $post = htmlspecialchars($post); // HTML 實體編碼
    return $post;
}
?>

三、現代最佳實務:預處理語句

addslashes()、mysql_real_escape_string() 皆非萬靈丹(多位元組繞過、編碼問題)。唯一正確做法:參數化查詢 / 預處理語句。

PDO (推薦)

<?php
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'user', 'pass', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => false, // 關鍵:使用真正預處理
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

// 查詢
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');
$stmt->execute([':email' => $email, ':status' => 1]);
$user = $stmt->fetch();

// 新增
$stmt = $pdo->prepare('INSERT INTO users (name, email, created_at) VALUES (:name, :email, NOW())');
$stmt->execute([':name' => $name, ':email' => $email]);
?id = $pdo->lastInsertId();
?>

MySQLi

<?php
$mysqli = new mysqli('127.0.0.1', 'user', 'pass', 'test');
$mysqli->set_charset('utf8mb4');

$stmt = $mysqli->prepare('SELECT * FROM users WHERE email = ?');
$stmt->bind_param('s', $email);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
$stmt->close();
?>

四、常見誤區與補充

  • 誤區:「過濾關鍵字就安全」→ 攻擊者可用編碼、註解、十六進位繞過
  • 誤區:「用 addslashes 就安全」→ 多位元組編碼 (GBK/Big5) 可繞過
  • 誤區:「ORM 就安全」→ 原生查詢或錯誤用法仍有風險
  • 正確:所有動態參數必須用預處理語句綁定,資料表/欄位名稱等識別碼無法綁定時,嚴格白名單驗證
  • 補充:資料庫帳號最小權限原則(僅需 SELECT/INSERT/UPDATE,無 DROP/ALTER)
  • 補充:WAF (Web Application Firewall) 作為第二道防線,非首要依賴

五、快速檢查清單

  1. ☐ 所有查詢使用 PDO/MySQLi 預處理語句
  2. ☐ 關閉 display_errors,啟用 log_errors
  3. ☐ open_basedir 限制存取範圍
  4. ☐ disable_functions 停用危險函數
  5. ☐ 資料庫帳號最小權限
  6. ☐ 定期進行滲透測試/代碼審計
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved