瀏覽器安全策略禁止 JavaScript 在 XMLHttpRequest 或 fetch 中設定 Referer、User-Agent、Cookie 等敏感標頭。若需模擬特定來源存取第三方 API(如域名查詢、支付回調測試),必須在**伺服器端以 CURL 代理**轉發請求。
<?php
// proxy.php — 通用 CURL 代理端點
$method = $_SERVER['REQUEST_METHOD'];
$targetUrl = $_GET['target_url'] ?? '';
if (!$targetUrl) die('Missing target_url');
$ch = curl_init($targetUrl);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_SSL_VERIFYPEER => false, // 僅測試環境;生產請驗證憑證
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_TIMEOUT => 15,
CURLOPT_CONNECTTIMEOUT => 5,
// 透傳標頭(排除 Host、Connection 等跳躍標頭)
CURLOPT_HTTPHEADER => array_filter([
'User-Agent: ' . ($_SERVER['HTTP_USER_AGENT'] ?? 'PHP Proxy'),
'Referer: ' . ($_GET['referer'] ?? ''),
'X-Requested-With: XMLHttpRequest',
'Accept: ' . ($_SERVER['HTTP_ACCEPT'] ?? '*/*'),
'Content-Type: ' . ($_SERVER['CONTENT_TYPE'] ?? 'application/x-www-form-urlencoded'),
]),
// 處理請求主體
CURLOPT_POSTFIELDS => ($method === 'POST') ? file_get_contents('php://input') : null,
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errno) {
http_response_code(502);
die(json_encode(['error' => 'CURL Error: ' . $error]));
}
// 透傳回應標頭(可選)
header('Content-Type: application/json; charset=utf-8');
http_response_code($httpCode);
echo $response;
?>
<?php
// check_domain.php — 前端 AJAX 呼叫此檔
$domain = $_GET['domain'] ?? ''; // 例:example
$tld = $_GET['tld'] ?? '.com';
$url = 'https://pandavip.www.net.cn/check/check_ac1.cgi';
$post = 'domain=' . urlencode($domain . $tld);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $post,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
CURLOPT_COOKIE => '__utma=...; __utmz=...', // 必要 Cookie
CURLOPT_HTTPHEADER => [
'X-Requested-With: XMLHttpRequest',
'Referer: https://pandavip.www.net.cn/cgi-bin/Check.cgi?queryType=0&domain1=' . $domain . '&domain=' . $domain . '&' . trim($tld, '.') . '=yes',
],
CURLOPT_TIMEOUT => 10,
]);
$result = curl_exec($ch);
curl_close($ch);
header('Content-Type: text/xml; charset=utf-8'); // 原 API 回傳 XML
echo $result;
?>
// 原本無法自訂 Referer 的前端程式碼
fetch('/check_domain.php?domain=example&tld=.com')
.then(r => r.text())
.then(xml => console.log(new DOMParser().parseFromString(xml, 'text/xml')));
| 風險 | 對策 |
|---|---|
| 開放式代理被濫用 | 僅允許白名單域名(parse_url($targetUrl, PHP_URL_HOST) 比對)、驗證 API Key、限制同 IP 頻率 |
| 敏感標頭洩漏 | 勿透傳 Authorization、Cookie 等內部標頭至外部 API |
| SSRF (Server-Side Request Forgery) | 禁止存取內網 IP(10.x、172.16-31.x、192.168.x、127.0.0.1、169.254.x)、封鎖 file://、gopher:// 協定 |
| 回應體過大導致記憶體耗盡 | 設定 CURLOPT_MAXFILESIZE、使用串流寫入暫存檔 |
若第三方 API 回應緩慢,可採用「發送請求 → 立即回傳 202 Accepted → 背景 Worker 執行 CURL → 結果存入 Redis → 前端輪詢/WebSocket 推送」架構,避免阻塞 PHP-FPM 進程。