正向代理:客戶端主動配置,代理替用戶訪問目標站(如 VPN、科學上網)。
反向代理:服務端部署,對外隱藏真實後端(如 Nginx 反代、CDN、負載均衡)。
惡意反向代理/鏡像:攻擊者在自己的域名下配置反向代理指向你的站點,用戶訪問攻擊者域名時,實際內容來自你的服務器,但攻擊者可:
| 層級 | 方案 | 優點 | 缺點 | 推薦指數 |
|---|---|---|---|---|
| 應用層 (PHP) | 校驗 $_SERVER['HTTP_HOST'] / SERVER_NAME | 邏輯靈活、可記錄日誌、可返回 JSON 錯誤 | 需 PHP 執行、性能稍低 | ⭐⭐⭐⭐⭐ |
| Web Server (Nginx/Apache) | 拒絕非法 Host、limit_req、geo 模組 | 極高性能、攔截最早、不佔 PHP 進程 | 配置相對複雜、調試較難 | ⭐⭐⭐⭐⭐ |
| 客戶端 (JavaScript) | 檢查 document.domain / top.location | 零服務端成本、可破 iframe 嵌套 | 可被禁用/繞過、對 SEO 無效 | ⭐⭐⭐ |
| DNS/邊緣 | Cloudflare/CloudFront 設定允許 Host、WAF 規則 | 流量進入前攔截、隱藏源站 IP | 需付費/配置、非自建環境適用 | ⭐⭐⭐⭐ |
在公共入口文件(如 public/index.php)或中間件最早處執行:
<?php
// config/allowed_hosts.php
return [
'example.com',
'www.example.com',
'api.example.com',
// 內網/健康檢查 IP 可選
'10.0.0.5',
'127.0.0.1',
];
// bootstrap/app.php 或 public/index.php 最頂部
$allowedHosts = require __DIR__ . '/../config/allowed_hosts.php';
// 獲取真實 Host(考慮反代頭)
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? '';
// 移除端口號
$host = preg_replace('/:\d+$/', '', $host);
if (!in_array($host, $allowedHosts, true)) {
// 記錄攻擊日誌
error_log(sprintf(
"[MIRROR_BLOCK] IP=%s Host=%s UA=%s URI=%s Time=%s",
$_SERVER['REMOTE_ADDR'] ?? 'unknown',
$host,
$_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
$_SERVER['REQUEST_URI'] ?? '/',
date('c')
));
// 根據請求類型返回不同響應
if (str_starts_with($_SERVER['REQUEST_URI'] ?? '', '/api/')) {
http_response_code(403);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['error' => 'Forbidden: Invalid host', 'code' => 403]);
} else {
http_response_code(403);
header('Content-Type: text/html; charset=utf-8');
echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>403 Forbidden</title></head><body>';
echo '<h1>403 - 非法訪問</h1><p>請通過官方域名訪問本站。</p>';
echo '</body></html>';
}
exit;
}
// 繼續正常啟動框架...
?>
function isHostAllowed(string $host, array $allowedPatterns): bool {
foreach ($allowedPatterns as $pattern) {
// 精確匹配
if ($pattern === $host) return true;
// 通配子域名 *.example.com
if (str_starts_with($pattern, '*.')) {
$suffix = substr($pattern, 1); // .example.com
if (str_ends_with($host, $suffix)) return true;
}
// 正則匹配(謹慎使用)
if (str_starts_with($pattern, '/') && str_ends_with($pattern, '/')) {
if (preg_match($pattern, $host)) return true;
}
}
return false;
}
$allowedPatterns = [
'example.com',
'www.example.com',
'*.example.com', // 所有子域名
'/^api-\d+\.example\.com$/', // 正則
];
?>
# nginx.conf / site.conf
server {
listen 80;
listen 443 ssl;
server_name example.com www.example.com; # 僅響應合法域名
# 關鍵:拒絕非法 Host(包括直接訪問 IP)
if ($host !~* ^(example\.com|www\.example\.com|api\.example\.com)$) {
return 444; # Nginx 特有:直接斷開連接,不返回任何響應
}
# 可選:限制單 IP 請求頻率防采集
limit_req_zone $binary_remote_addr zone=mirror:10m rate=30r/s;
limit_req zone=mirror burst=50 nodelay;
location / {
proxy_pass https://php_upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# 另開一個 server 捕獲所有非法 Host(可選,用於記錄日誌)
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_certificate /path/selfsigned.crt;
ssl_certificate_key /path/selfsigned.key;
return 444;
}
# .htaccess (需開啟 AllowOverride All)
RewriteEngine On
# 僅允許合法域名
RewriteCond %{HTTP_HOST} !^(example\.com|www\.example\.com|api\.example\.com)$ [NC]
RewriteRule ^ - [F,L] # 返回 403
# 或重導向到官方域名(保留路徑)
# RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
# RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]
<script>
// 1. 防止被 iframe 嵌套
if (window.top !== window.self) {
window.top.location = window.self.location;
}
// 2. 檢查域名是否為官方域名(配合 PHP 雙重保險)
(function() {
var allowed = ['example.com', 'www.example.com', 'api.example.com'];
var hostname = window.location.hostname.replace(/:\d+$/, ''); // 去端口
if (!allowed.includes(hostname) && !hostname.endsWith('.example.com')) {
// 延遲跳轉避免被鏡像站 JS 攔截
setTimeout(function() {
window.location.href = 'https://www.example.com' + window.location.pathname + window.location.search;
}, 100);
}
})();
</script>
在 Nginx/PHP 統一輸出以下安全標頭:
# 防止被 iframe 嵌套(現代瀏覽器支援)
add_header X-Frame-Options "DENY" always;
# 或更靈活的 CSP frame-ancestors
add_header Content-Security-Policy "frame-ancestors 'none';" always;
# 防 MIME 類型嗅探
add_header X-Content-Type-Options "nosniff" always;
# 強制 HTTPS(HSTS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
$_SERVER['HTTP_HOST'] 可能被竄改,建議在 Nginx 設定 proxy_set_header Host $host; 並信任 X-Forwarded-Host(需配置 set_real_ip_from)。┌─────────────────────────────────────┐ │ DNS/邊緣層 (Cloudflare/WAF) │ ← 首選:隱藏源站、預置規則攔截 ├─────────────────────────────────────┤ │ Web Server 層 (Nginx/Apache) │ ← 核心:高性能 Host 白名單、444 斷連 ├─────────────────────────────────────┤ │ 應用層 │ ← 兜底:邏輯靈活、記錄詳盡、API 友好 ├─────────────────────────────────────┤ │ 客戶端層 │ ← 輔助:破 iframe、用戶端跳轉 └─────────────────────────────────────┘
採用 Nginx 白名單 + PHP 兜底 + 安全標頭 + 可選 JS 的組合拳,可將惡意鏡像站的生存空間壓縮至最小,保護流量、SEO 與用戶安全。