首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP IP 限制投票機制:X-Forwarded-For 偽造風險與防禦策略

     編輯:PHP 相關知識

PHP IP 限制投票機制:X-Forwarded-For 偽造風險與防禦策略

在開發線上投票、問卷或限流功能時,常需限制同一 IP 的操作次數。許多開發者直接採用網路上流傳的「取得真實 IP」程式碼,卻忽略了 X-Forwarded-For 標頭極易偽造的風險,導致惡意使用者繞過限制。本文深入分析 IP 取得邏輯漏洞,並提供安全可靠的實作方案。

常見但不安全的取得 IP 寫法

function getClientIp() {
    if (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
        return getenv('HTTP_CLIENT_IP');
    } elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
        return getenv('HTTP_X_FORWARDED_FOR');
    } elseif (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
        return getenv('REMOTE_ADDR');
    }
    return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}

此邏輯優先信任 HTTP_X_FORWARDED_FOR,攻擊者只需在請求標頭加入 X-Forwarded-For: 255.255.255.255 即可偽造任意 IP。

實測偽造示範

使用 PHP Socket 發送偽造標頭:

$head = [
    "GET /vote.php HTTP/1.1",
    "Host: localhost",
    "X-Forwarded-For: 255.255.255.255",
    "Connection: Close",
    ""
];
$fp = fsockopen('localhost', 80);
fwrite($fp, implode("\r\n", $head));
while ($buff = fread($fp, 4096)) echo $buff;

若伺服器端採用上述不安全函式,將回傳偽造的 IP,導致限流失效。

安全的 IP 取得策略

方案一:僅信任 REMOTE_ADDR(最簡單、最安全)

function getRealIp() {
    return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}

REMOTE_ADDR 由 Web Server(Nginx/Apache)根據 TCP 連線來源決定,無法由客戶端偽造。雖然無法穿透反向代理取得真實用戶 IP,但對於「限制投票次數」此類需求,限制代理出口 IP 反而更有效,因為攻擊者無法輕易更換出口 IP。

方案二:受信任代理鏈下的真實 IP 取得(需架構配合)

若系統部署在雲端負載平衡器(如 AWS ALB、Cloudflare)之後,且確定代理會覆寫或正確附加 X-Forwarded-For,可採用:

function getTrustedClientIp(array $trustedProxies = ['10.0.0.0/8', '172.16.0.0/12']) {
    $remote = $_SERVER['REMOTE_ADDR'] ?? '';
    // 確認請求來自受信任代理
    foreach ($trustedProxies as $cidr) {
        if (ipInCidr($remote, $cidr)) {
            $xff = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
            // 取最左邊(原始客戶端)或最右邊(最後一個代理)視代理策略而定
            $ips = array_map('trim', explode(',', $xff));
            return $ips[0] ?? $remote; // 假設代理將真實 IP 放在最左
        }
    }
    return $remote;
}

function ipInCidr($ip, $cidr) {
    list($subnet, $mask) = explode('/', $cidr);
    return (ip2long($ip) & ~((1 << (32 - $mask)) - 1)) == ip2long($subnet);
}

關鍵前提:$trustedProxies 必須精確對應實際基礎設施,否則仍有偽造風險。

投票限流完整實作範例(Redis 計數器)

<?php
require 'vendor/autoload.php';
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$ip = getRealIp(); // 使用方案一
$voteKey = "vote:limit:{$ip}";
$maxVotes = 3; // 每 IP 最多 3 票
$ttl = 86400; // 24 小時重置

$current = $redis->incr($voteKey);
if ($current === 1) {
    $redis->expire($voteKey, $ttl);
}

if ($current > $maxVotes) {
    http_response_code(429);
    exit(json_encode(['error' => '您今天已達投票上限', 'remaining' => 0]));
}

// 處理投票邏輯...
$redis->decr($voteKey); // 若投票失敗需回滾,或改用 Lua 腳本原子操作
echo json_encode(['success' => true, 'remaining' => $maxVotes - $current]);

進階防禦建議

  • 裝置指紋:結合 Canvas 指紋、User-Agent、Cookie 等多維度識別同一使用者。
  • 行為分析:偵測短時間大量請求、固定間隔請求等機器特徵。
  • 驗證碼/滑塊驗證:疑似異常流量觸發二次驗證。
  • 帳號綁定:重要投票需登入會員,綁定手機/Email,提高作弊成本。
  • 日誌稽核:記錄所有投票請求的 IP、UA、Referer、時間,事後分析異常模式。

總結

IP 限制是基礎防線,非絕對防禦。切勿信任任何客戶端可控標頭(X-Forwarded-For, X-Real-IP, Client-IP 等),除非你完全掌控前端代理鏈且能保證其安全性。對於投票類應用,結合「出口 IP 限流 + 行為分析 + 驗證碼 + 帳號綁定」的多層次防護,才能有效維護活動公平性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved