PHP 作為 Web 開發主流語言,其靈活性也帶來安全隱患。本文構建 輸入驗證、輸出編碼、資料庫防注入、會話管理、錯誤處理 五大防護體系,提供可直接落地的代碼模式與審計清單。
<?php
// 1. 定義嚴格驗證規則 (建議使用 Respect/Validation 或 Laravel Validator)
function validateInput(array $input, array $rules): array {
$errors = [];
foreach ($rules as $field => $rule) {
$value = $input[$field] ?? null;
// 型別檢查
if (str_starts_with($rule, 'type:')) {
$type = substr($rule, 5);
if (!validateType($value, $type)) {
$errors[$field] = "欄位 $field 型別錯誤,需為 $type";
}
}
// 長度/範圍/格式...
}
if ($errors) throw new ValidationException($errors);
return array_intersect_key($input, $rules); // 白名單過濾
}
function validateType($value, string $type): bool {
return match($type) {
'int' => filter_var($value, FILTER_VALIDATE_INT) !== false,
'email' => filter_var($value, FILTER_VALIDATE_EMAIL) !== false,
'url' => filter_var($value, FILTER_VALIDATE_URL) !== false,
'alpha' => ctype_alpha($value),
'alnum' => ctype_alnum($value),
'string' => is_string($value),
default => true,
};
}
// 2. 使用場景:控制器入口
try {
$data = validateInput($_POST, [
'username' => 'type:alnum|max:20',
'email' => 'type:email',
'age' => 'type:int|min:18|max:120',
]);
} catch (ValidationException $e) {
return apiError(422, $e->getErrors());
}
?>
; 必須關閉的歷史遺留特性
register_globals = Off ; 防止變量注入
magic_quotes_gpc = Off ; 避免雙重轉義導致邏輯錯誤
magic_quotes_runtime = Off
magic_quotes_sybase = Off
allow_url_fopen = Off ; 禁用遠程文件包含 (RFU)
allow_url_include = Off
expose_php = Off ; 隱藏 PHP 版本資訊
display_errors = Off ; 生產環境禁止顯示錯誤
log_errors = On
error_log = /var/log/php/error.log
max_execution_time = 30
memory_limit = 128M
post_max_size = 8M
upload_max_filesize = 2M
file_uploads = On
upload_tmp_dir = /tmp/php_upload (獨立目錄,權限 700)
session.cookie_httponly = 1 ; 防 XSS 竊取 Session
session.cookie_secure = 1 ; 僅 HTTPS 傳輸 Cookie
session.use_strict_mode = 1 ; 防 Session Fixation
session.sid_length = 32
session.sid_bits_per_character = 5
<?php
// 統一輸出助手 (模板引擎應內建此功能,如 Blade {{ }}, Twig {{ }}, Latte {= })
function h(string $str, int $flags = ENT_QUOTES | ENT_SUBSTITUTE, string $encoding = 'UTF-8'): string {
return htmlspecialchars($str, $flags, $encoding);
}
// JavaScript 內輸出需額外編碼
function jsEscape(string $str): string {
return json_encode($str, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
}
// CSS/URL 等上下文編碼...
// 使用場景:模板中
// <div><?= h($user->name) ?></div>
// <script>const user = <?= jsEscape($user->name) ?>;</script>
?>
<?php
// 強制使用預處理語句 (PDO)
$pdo = new PDO('mysql:host=localhost;dbname=app;charset=utf8mb4', '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 = ? AND status = ?');
$stmt->execute([$email, 1]);
$user = $stmt->fetch();
// 批量插入
$pdo->beginTransaction();
$stmt = $pdo->prepare('INSERT INTO logs (user_id, action, ip) VALUES (?, ?, ?)');
foreach ($actions as $action) {
$stmt->execute([$userId, $action, $ip]);
}
$pdo->commit();
// 禁止動態拼接 SQL 的場景:表名、欄位名、排序方向
// 必須使用白名單驗證
$allowedSort = ['created_at', 'updated_at', 'name'];
$sort = in_array($_GET['sort'] ?? '', $allowedSort) ? $_GET['sort'] : 'created_at';
$order = ($_GET['order'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC';
$stmt = $pdo->prepare("SELECT * FROM posts ORDER BY $sort $order"); // 欄位名經白名單驗證安全
?>
| 風險 | 防護措施 | 代碼/配置 |
|---|---|---|
| Session Fixation | 登入後重新生成 ID、啟用 strict_mode | session_regenerate_id(true); / session.use_strict_mode=1 |
| Session Hijacking | 僅 HTTPS、HttpOnly、SameSite、短過期、IP/UA 綁定 | session_set_cookie_params(['secure'=>true,'httponly'=>true,'samesite'=>'Strict']); |
| CSRF | 同步令牌模式、SameSite Cookie、雙重提交 Cookie | <input type="hidden" name="_token" value="<?= csrf_token() ?>"> |
| 暴力破解 | 限流、驗證碼、帳號鎖定、多因子認證 (TOTP/WebAuthn) | Redis 計數器 + 指數退避鎖定 |
| 密碼洩露 | Argon2id 雜湊、禁止常用密碼、洩露檢測 (HaveIBeenPwned API) | password_hash($pwd, PASSWORD_ARGON2ID) |
<?php
// 生產環境錯誤處理器
set_error_handler(function($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) return false;
throw new ErrorException($message, 0, $severity, $file, $line);
});
set_exception_handler(function(Throwable $e) {
// 記錄完整堆疊
error_log(sprintf(
"[%s] %s in %s:%d\nStack:\n%s",
date('c'), $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString()
));
// 用戶友好頁面
if (ob_get_level()) ob_clean();
http_response_code(500);
include __DIR__ . '/templates/500.php'; // 靜態頁面,不執行邏輯
exit;
});
// 關閉顯示錯誤
ini_set('display_errors', '0');
ini_set('display_startup_errors', '0');
?>
htmlspecialchars 或模板引擎自動逃逸extract()、$$var 等動態變量eval()、assert()、create_function()、preg_replace('/e') 等動態執行composer audit、npm audit、定期更新config, storage, vendor) 有 .htaccess/nginx 禁止訪問規則安全不是功能,而是贯穿設計、開發、部署、運維全生命週期的工程體系。
PHP 內建函數 (filter_var,password_hash,htmlspecialchars, PDO) 已提供強大基礎設施,關鍵在於 紀律性地正確使用。