前言
DDoS、DNS 劫持、叢集攻擊屬於網路/傳輸層與基礎設施層威脅,PHP 應用層無法從根源阻斷,但可通過應用層識別、限流、降級、熔斷、蜜罐、日誌審計等手段緩解影響、保護核心業務可用性。本文提供可直接落地的 PHP 代碼與架構策略。
威脅模型與應用層可見症狀
| 攻擊類型 | 網路層特徵 | 應用層可見症狀 | PHP 可做什麼 |
| 體積型 DDoS (UDP/ICMP/SYN Flood) | 頻寬耗盡、連接表滿 | 請求超時、502/504、PHP-FPM 進程耗盡 | 快速拒絕、靜態化、熔斷下游 |
| 應用層 DDoS (CC 攻擊、HTTP Flood) | 大量合法 HTTP 請求 | CPU/記憶體飆升、資料庫連接池耗盡、業務邏輯阻塞 | 多維限流、驗證碼、請求去重、緩存穿透防護 |
| DNS 劫持/投毒 | 域名解析到惡意 IP | 用戶訪問被導向釣魚站、流量劫持 | HSTS、HPKP、子資源完整性 (SRI)、客戶端憑證綁定 |
| 叢集/僵屍網絡協同攻擊 | 分散式 IP、低頻慢速 | 傳統單 IP 限流失效、行為異常難檢測 | 裝置指紋、行為分析、風控引擎、蜜罐陷阱 |
核心代碼模組:多維度請求守衛
<?php
/**
* 應用層防禦中介軟體(可整合至 Laravel/ThinkPHP/Yii/Slim/原生)
*/
class AppDefense {
private Redis $redis;
private array $config;
public function __construct(Redis $redis, array $config = []) {
$this->redis = $redis;
$this->config = array_merge([
'ip_whitelist' => [],
'ip_blacklist_ttl' => 3600,
'rate_limit' => [
'global' => [1000, 60], // 全站:1000 req/min
'per_ip' => [60, 60], // 單 IP:60 req/min
'per_ip_uri' => [20, 60], // 單 IP+URI:20 req/min
'login' => [5, 300], // 登入:5 req/5min
'register' => [3, 3600], // 註冊:3 req/hr
],
'ban_threshold' => 100, // 觸發封禁的違規次數
'ban_duration' => 1800, // 封禁 30 分鐘
'honeypot_fields' => ['website', 'url', 'homepage'], // 蜜罐欄位名
], $config);
}
/** 主入口:在路由分發前調用 */
public function handle(): void {
$ip = $this->getRealIp();
$uri = $_SERVER['REQUEST_URI'] ?? '/';
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
// 1. 白名單直通
if (in_array($ip, $this->config['ip_whitelist'], true)) return;
// 2. 黑名單攔截
if ($this->redis->exists("ban:ip:$ip")) {
$this->respond(403, 'IP 已被封禁');
}
// 3. 蜜罐檢測(隱藏表單欄位若被填寫 = 機器人)
if ($method === 'POST') {
foreach ($this->config['honeypot_fields'] as $field) {
if (!empty($_POST[$field] ?? '')) {
$this->recordViolation($ip, 'honeypot');
$this->respond(403, 'Forbidden');
}
}
}
// 4. 多維滑動窗口限流
$limits = $this->config['rate_limit'];
$this->checkLimit("ratelimit:global", $limits['global'][0], $limits['global'][1]);
$this->checkLimit("ratelimit:ip:$ip", $limits['per_ip'][0], $limits['per_ip'][1]);
$this->checkLimit("ratelimit:ip_uri:$ip:$uri", $limits['per_ip_uri'][0], $limits['per_ip_uri'][1]);
// 5. 敏感操作專用限流
if (str_contains($uri, '/login')) {
$this->checkLimit("ratelimit:login:$ip", $limits['login'][0], $limits['login'][1]);
}
if (str_contains($uri, '/register')) {
$this->checkLimit("ratelimit:register:$ip", $limits['register'][0], $limits['register'][1]);
}
// 6. 記錄存活心跳(用於事後分析)
$this->redis->zAdd("alive:ip:$ip", microtime(true), microtime(true));
$this->redis->expire("alive:ip:$ip", 300);
}
/** Redis 滑動窗口限流 */
private function checkLimit(string $key, int $limit, int $window): void {
$now = microtime(true);
$pipe = $this->redis->pipeline();
$pipe->zRemRangeByScore($key, 0, $now - $window);
$pipe->zCard($key);
$pipe->zAdd($key, $now, (string)$now);
$pipe->expire($key, $window + 1);
$results = $pipe->exec();
if ($results[1] >= $limit) {
$this->recordViolation($this->getRealIp(), 'ratelimit');
$this->respond(429, '請求過於頻繁,請稍後再試');
}
}
/** 違規記錄與自動封禁 */
private function recordViolation(string $ip, string $type): void {
$key = "violation:ip:$ip";
$count = $this->redis->incr($key);
if ($count === 1) $this->redis->expire($key, 86400); // 24 小時統計窗
if ($count >= $this->config['ban_threshold']) {
$this->redis->setex("ban:ip:$ip", $this->config['ban_duration'], $type);
error_log("AUTO-BAN IP: $ip, type: $type, count: $count");
}
}
/** 獲取真實客戶端 IP(支援代理鏈) */
private function getRealIp(): string {
$headers = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP'];
foreach ($headers as $h) {
if (!empty($_SERVER[$h])) {
$ips = array_map('trim', explode(',', $_SERVER[$h]));
foreach ($ips as $ip) {
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return $ip;
}
}
}
}
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
/** 統一響應並終止 */
private function respond(int $code, string $msg): void {
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['error' => $msg, 'code' => $code]);
exit;
}
}
// 使用範例(原生 PHP 入口)
require 'vendor/autoload.php';
$redis = new Redis(); $redis->connect('127.0.0.1', 6379);
$defense = new AppDefense($redis);
$defense->handle();
// 之後才執行路由/業務邏輯
?>
架構層面緩解策略
1. 靜態化與邊緣緩存(抗體積型 DDoS 核心)
- 全站靜態頁面推送至 CDN(Cloudflare、CloudFront、阿里雲 CDN)
- 動態 API 採用
Cache-Control: public, max-age=10, stale-while-revalidate=60
- CDN 開啟「Under Attack 模式」(5 秒 JS 挑戰)
2. 服務降級與熔斷
<?php
// 熔斷器簡易實現
class CircuitBreaker {
private Redis $redis;
public function __construct(Redis $redis) { $this->redis = $redis; }
public function call(string $service, callable $fn, callable $fallback) {
$key = "circuit:$service";
$state = $this->redis->get($key) ?: 'closed'; // closed/open/half-open
if ($state === 'open') {
// 檢查是否進入半開狀態
if ($this->redis->ttl($key) < 10) {
$this->redis->set($key, 'half-open', 'EX', 30);
} else {
return $fallback();
}
}
try {
$result = $fn();
if ($state === 'half-open') $this->redis->del($key); // 恢復
return $result;
} catch (Throwable $e) {
$failures = $this->redis->incr("$key:fails");
if ($failures >= 5) {
$this->redis->set($key, 'open', 'EX', 60); // 熔斷 60 秒
}
return $fallback();
}
}
}
// 使用
$breaker = new CircuitBreaker($redis);
$userInfo = $breaker->call('user-service',
fn() => $httpClient->get("/api/user/$uid"),
fn() => ['id' => $uid, 'name' => 'Guest', 'degraded' => true] // 降級回退
);
?>
3. DNS 劫持防護(客戶端 + 伺服端雙重)
| 層面 | 措施 |
| 伺服端 | 強制 HTTPS + HSTS (Strict-Transport-Security: max-age=31536000; includeSubDomains; preload) |
| 伺服端 | HPKP (Public Key Pinning) —— 風險高,謹慎用 |
| 前端 | Subresource Integrity (SRI):<script src="..." integrity="sha384-..." crossorigin="anonymous"> |
| 前端 | CSP 禁止內聯腳本、限制來源域名 |
| App/小程序 | 憑證綁定、域名白名單、偵測 DNS 解析異常彈窗提示 |
4. 蜜罐與主動誘捕
- 隱藏表單欄位(CSS
display:none、visibility:hidden、偏移視窗外)
- 偽造管理後台路徑
/admin/login.php、/phpmyadmin/ 記錄掃描者 IP
- 偽造 API 端點
/api/v1/users/export 回傳假資料、記錄請求
- 將蜜罐觸發 IP 直接加入黑名單、同步威脅情報平台
監控與告警指標
| 指標 | 正常基線 | 告警閾值 | 響應 |
| QPS (請求/秒) | 業務相關 | > 基線 3 倍 | 啟用 Under Attack、擴容 PHP-FPM |
| PHP-FPM 活躍進程數 | < pm.max_children | > 80% | 調優慢查詢、啟用靜態化、限流 |
| 資料庫連接數 | < max_connections | > 80% | 啟用連接池、讀寫分離、熔斷 |
| 錯誤率 (5xx) | < 0.1% | > 1% | 檢查日誌、回滾發布、熔斷下游 |
| 獨立 IP 數/分鐘 | 業務相關 | 突增 5 倍 | 疑似 CC 攻擊、啟用驗證碼、封禁異常 IP 段 |
總結
PHP 應用層無法「防禦」DDoS/DNS 劫持,但能通過識別異常流量、限流保護核心資源、熔斷防止雪崩、靜態化轉移壓力、蜜罐主動誘捕、日誌留存溯源將影響降至最低。真正的抗 DDoS 必須在網路邊界(Anycast 清洗、CDN 邊緣吸收、WAF 規則攔截)解決。建議:應用層代碼作為「最後一道防線」,基礎設施層作為「主戰場」,兩者配合定期壓測、紅藍對抗演練,構建韌性架構。