請求鏈路:`Client → [Proxy/CDN/WAF] → Load Balancer → Web Server`。每一層都可能插入/覆蓋 `X-Forwarded-For`、`X-Real-IP`、`True-Client-IP` 等標頭。伺服器端 `Request.UserHostAddress` 僅見**直接上游 IP** (通常為 LB 內網 IP)。
| 方法 | 程式碼 | 適用場景 | 可信度 |
|---|---|---|---|
| 1. 直接屬性 | `Request.UserHostAddress` | 無代理、內網直連 | ★☆☆☆☆ (見上游 IP) |
| 2. ServerVariables | `Request.ServerVariables["REMOTE_ADDR"]` | 同方法 1 | ★☆☆☆☆ |
| 3. DNS 反查 (錯誤示範) | `Dns.GetHostAddresses(Dns.GetHostName())` | **錯誤**:取得伺服器自身 IP,非客戶端 | ✖️ |
| 4. X-Forwarded-For (標準) | `Request.ServerVariables["HTTP_X_FORWARDED_FOR"]` | 單層代理、信任代理標頭 | ★★★☆☆ (需驗證代理可信) |
| 5. 客戶端 JS 回傳 | `var ip = '';` (SSI) 或 AJAX 送 `/api/myip` | 頁面嵌入、需 JS 執行 | ★★☆☆☆ (可被偽造) |
| 6. ActiveX (舊 IE 專用) | `new ActiveXObject("rcbdyctl.Setting").GetIPAddress` | 極舊環境 | ✖️ (已淘汰) |
public static class ClientIpResolver
{
// 可信代理 IP 範圍 (CIDR),例如雲端 LB、公司 Proxy、CDN 邊緣節點
private static readonly string[] TrustedProxyCidrs =
{
"10.0.0.0/8", // 內網
"172.16.0.0/12", // 內網
"192.168.0.0/16", // 內網
"100.64.0.0/10", // CGNAT / AWS ALB
// 加入具體 CDN/LB IP 範圍 (如 Cloudflare: 173.245.48.0/20 ...)
};
private static readonly IPNetwork[] _trustedNetworks =
TrustedProxyCidrs.Select(c => IPNetwork.Parse(c)).ToArray();
public static string GetClientIp(HttpRequest request)
{
// 1. 取得直接連線 IP
string remoteIp = request.UserHostAddress; // 或 request.ServerVariables["REMOTE_ADDR"]
// 2. 若直接 IP 不在可信代理清單 → 視為真實客戶端 (無代理或不可信代理)
if (!_trustedNetworks.Any(n => n.Contains(IPAddress.Parse(remoteIp))))
return remoteIp;
// 3. 直接 IP 為可信代理 → 解析 X-Forwarded-For
string xff = request.Headers["X-Forwarded-For"]
?? request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (string.IsNullOrWhiteSpace(xff))
return remoteIp; // 代理未傳標頭,回退
// 4. XFF 格式: "client, proxy1, proxy2" (最左為原始客戶端)
// 從右向左跳過可信代理,第一個不可信即為真實客戶端
var ips = xff.Split(',').Select(s => s.Trim()).ToArray();
for (int i = ips.Length - 1; i >= 0; i--)
{
if (IPAddress.TryParse(ips[i], out var ip) && !_trustedNetworks.Any(n => n.Contains(ip)))
return ips[i];
}
// 5. 全鏈路皆可信 (極罕見) → 取最左
return ips.FirstOrDefault() ?? remoteIp;
}
}
// 輔助:CIDR 判斷 (需引用 System.Net.IPNetwork 或自行實作)
public class IPNetwork
{
public IPAddress NetworkAddress { get; }
public int PrefixLength { get; }
public static IPNetwork Parse(string cidr) { /* 解析網址/前綴長度 */ }
public bool Contains(IPAddress address) { /* 位元運算判斷 */ }
}
| 平台 | 標頭 | 備註 |
|---|---|---|
| AWS ALB / CloudFront | `X-Forwarded-For` (標準)、`X-Amzn-Trace-Id` | ALB 固定加在最右,CloudFront 在最左 |
| Azure Front Door / App Gateway | `X-Forwarded-For`、`X-Azure-ClientIP` | `X-Azure-ClientIP` 為 Azure 專屬真實 IP |
| Google Cloud Load Balancing | `X-Forwarded-For`、`X-Cloud-Trace-Context` | |
| Cloudflare | `CF-Connecting-IP` (真實 IP)、`X-Forwarded-For` | `CF-Connecting-IP` 最可信,僅 Cloudflare 設定 |
| Akamai | `True-Client-IP`、`X-Forwarded-For` | `True-Client-IP` 為 Akamai 專屬 |
| Nginx/HAProxy (自建) | `X-Real-IP` (單一)、`X-Forwarded-For` (鏈) | 需在設定 `proxy_set_header X-Real-IP $remote_addr;` |
// Program.cs / Startup.cs
services.Configure(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// 必須明確加入已知代理網段,否則中間件會忽略標頭 (安全預設)
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
foreach (var cidr in ClientIpResolver.TrustedProxyCidrs)
options.KnownNetworks.Add(IPNetwork.Parse(cidr));
});
app.UseForwardedHeaders(); // 必須在 UseAuthentication/UseAuthorization 之前
// Controller 中
string clientIp = HttpContext.Connection.RemoteIpAddress?.ToString(); // 已自動解析 XFF
**無代理**:`Request.UserHostAddress` 即可。
**單層可信代理**:`Request.Headers["X-Forwarded-For"]` 取最左。
**多層/雲端/混合**:**部署 `ForwardedHeadersMiddleware` (Core) 或自寫 `ClientIpResolver` (Framework),配合可信 CIDR 清單從右向左解析 XFF**。
**客戶端 JS/ActiveX 方案已淘汰**,不可用於安全決策。統一全站 HTTPS + 標頭驗證,才是正道。