web-dev-qa-db-ja.com

PHPでユーザーの正しいIPアドレスを取得する最も正確な方法は何ですか?

IPアドレスの取得に使用できる$ _ SERVER変数ヘッダーが多すぎることは知っています。上記の変数を使用してユーザーの実際のIPアドレスを最も正確に取得する方法(完璧な方法はないことをよく知っている)について一般的なコンセンサスがあるかどうか疑問に思っていましたか?

綿密な解決策を見つけるために時間を費やし、いくつかのソースに基づいて次のコードを思い付きました。誰かが答えに穴を開けてくれたり、おそらくもっと正確なものに光を当ててくれたら、私はそれが大好きです。

editには@Alixからの最適化が含まれます

 /**
  * Retrieves the best guess of the client's actual IP address.
  * Takes into account numerous HTTP proxy headers due to variations
  * in how different ISPs handle IP addresses in headers between Hops.
  */
 public function get_ip_address() {
  // Check for shared internet/ISP IP
  if (!empty($_SERVER['HTTP_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_CLIENT_IP']))
   return $_SERVER['HTTP_CLIENT_IP'];

  // Check for IPs passing through proxies
  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
   // Check if multiple IP addresses exist in var
    $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    foreach ($iplist as $ip) {
     if ($this->validate_ip($ip))
      return $ip;
    }
   }
  }
  if (!empty($_SERVER['HTTP_X_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_X_FORWARDED']))
   return $_SERVER['HTTP_X_FORWARDED'];
  if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
   return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
  if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && $this->validate_ip($_SERVER['HTTP_FORWARDED_FOR']))
   return $_SERVER['HTTP_FORWARDED_FOR'];
  if (!empty($_SERVER['HTTP_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_FORWARDED']))
   return $_SERVER['HTTP_FORWARDED'];

  // Return unreliable IP address since all else failed
  return $_SERVER['REMOTE_ADDR'];
 }

 /**
  * Ensures an IP address is both a valid IP address and does not fall within
  * a private network range.
  *
  * @access public
  * @param string $ip
  */
 public function validate_ip($ip) {
     if (filter_var($ip, FILTER_VALIDATE_IP, 
                         FILTER_FLAG_IPV4 | 
                         FILTER_FLAG_IPV6 |
                         FILTER_FLAG_NO_PRIV_RANGE | 
                         FILTER_FLAG_NO_RES_RANGE) === false)
         return false;
     self::$ip = $ip;
     return true;
 }

警告の言葉(更新)

REMOTE_ADDRは、IPアドレスの最も信頼できるソースを表します。ここで説明した他の$_SERVER変数は、リモートクライアントによって非常に簡単になりすますことができます。このソリューションの目的は、プロキシの背後に座っているクライアントのIPアドレスを特定しようとすることです。一般的な目的のために、$_SERVER['REMOTE_ADDR']から直接返されたIPアドレスと組み合わせて使用​​し、両方を保存することを検討できます。

99.9%のユーザーにとって、このソリューションはニーズに完全に適合します。独自のリクエストヘッダーを挿入することでシステムを悪用しようとする悪意のあるユーザーの0.1%からは保護されません。ミッションクリティカルな何かのためにIPアドレスに依存している場合は、REMOTE_ADDRに頼って、プロキシの背後にいる人に対応する必要はありません。

275
Corey Ballou

これはIPアドレスを取得するための、より短く、よりきれいな方法です:

function get_ip_address(){
    foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){
        if (array_key_exists($key, $_SERVER) === true){
            foreach (explode(',', $_SERVER[$key]) as $ip){
                $ip = trim($ip); // just to be safe

                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
                    return $ip;
                }
            }
        }
    }
}

私はそれが役立つことを願っています!


あなたのコードはすでにかなり完成しているようです。(通常のIPの注意点を除けば)可能なバグは見当たりませんが、validate_ip()関数を変更してフィルター拡張に依存します。

public function validate_ip($ip)
{
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false)
    {
        return false;
    }

    self::$ip = sprintf('%u', ip2long($ip)); // you seem to want this

    return true;
}

また、HTTP_X_FORWARDED_FORスニペットはこれから単純化できます:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    // check if multiple ips exist in var
    if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false)
    {
        $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

        foreach ($iplist as $ip)
        {
            if ($this->validate_ip($ip))
                return $ip;
        }
    }

    else
    {
        if ($this->validate_ip($_SERVER['HTTP_X_FORWARDED_FOR']))
            return $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
}

これに:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

    foreach ($iplist as $ip)
    {
        if ($this->validate_ip($ip))
            return $ip;
    }
}

IPv6アドレスを検証することもできます。

234
Alix Axel

それでも、ユーザーの実際のIPアドレスを取得することは信頼できません。彼らがする必要があるのは、匿名プロキシサーバー(http_x_forwarded_for、http_forwardedなどのヘッダーを尊重しないサーバー)を使用することだけで、取得するのはプロキシサーバーのIPアドレスだけです。

その後、匿名のプロキシサーバーIPアドレスのリストがあるかどうかを確認できますが、100%正確であることを確認する方法はなく、それがプロキシサーバーであることを知らせることです。そして、誰かが巧妙であれば、HTTP転送用のヘッダーをスプーフィングできます。

私は地元の大学が好きではないとしましょう。彼らが登録したIPアドレスを把握し、悪いことをすることであなたのサイトで彼らのIPアドレスを禁止します。なぜなら、HTTP転送を尊重するからです。リストは無限です。

その後、ご想像のとおり、以前に会った大学のネットワークなどの内部IPアドレスがあります。多くの場合、10.x.x.x形式を使用します。知っているのは、共有ネットワークに転送されたことだけです。

それから私はそれにあまり始めませんが、動的IPアドレスはもはやブロードバンドの方法です。そう。ユーザーIPアドレスを取得した場合でも、最長で2〜3か月で変更されることを期待してください。

10
user192230

を使用しております:

/**
 * Get the customer's IP address.
 *
 * @return string
 */
public function getIpAddress() {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        return $_SERVER['HTTP_CLIENT_IP'];
    } else if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim($ips[count($ips) - 1]);
    } else {
        return $_SERVER['REMOTE_ADDR'];
    }
}

HTTP_X_FORWARDED_FORの爆発は、 Squid が使用されたときにIPアドレスを検出していた奇妙な問題のためです。

7
gabrielk

私の答えは基本的に、@ AlixAxelの答えの洗練された、完全に検証され、完全にパッケージ化されたバージョンです。

<?php

/* Get the 'best known' client IP. */

if (!function_exists('getClientIP'))
    {
        function getClientIP()
            {
                if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) 
                    {
                        $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
                    };

                foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key)
                    {
                        if (array_key_exists($key, $_SERVER)) 
                            {
                                foreach (explode(',', $_SERVER[$key]) as $ip)
                                    {
                                        $ip = trim($ip);

                                        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
                                            {
                                                return $ip;
                                            };
                                    };
                            };
                    };

                return false;
            };
    };

$best_known_ip = getClientIP();

if(!empty($best_known_ip))
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip;
    }
else
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip = '';
    };

?>

変更点:

  • 関数名を簡略化します(「camelCase」フォーマットスタイルを使用)。

  • コードの別の部分で関数がまだ宣言されていないことを確認するチェックが含まれています。

  • 「CloudFlare」の互換性が考慮されます。

  • 'getClientIP'関数の複数の「IP関連」変数名を戻り値に初期化します。

  • 関数が有効なIPアドレスを返さない場合、すべての変数はnullではなく空の文字列に設定されます。

  • コードはわずか(45)行です。

4

最大の質問は何のためですか?

あなたのコードは可能な限り包括的です-しかし、プロキシが追加されたヘッダーのように見えるものを見つけた場合、CLIENT_IPのそのINSTEADを使用しますが、監査目的でこの情報が必要な場合は警告されます-非常に簡単です偽物に。

確かに、どんな種類の認証にもIPアドレスを決して使用すべきではありません-たとえこれらが偽造されてもかまいません。

クライアントのIPアドレスをより適切に測定するには、フラッシュまたはJavaアプレットをプッシュして、http以外のポートを介してサーバーに接続し直します(したがって、透過プロキシまたはプロキシ-挿入されたヘッダーはfalseです-ただし、クライアントがWebプロキシ経由でのみ接続できる場合、または発信ポートがブロックされている場合、アプレットからの接続はないことに注意してください。

C.

2
symcbean

上記の方がはるかに優れた簡潔な回答がありますが、これは機能でもなければ、最も優雅なスクリプトでもありません。この場合、単純なスイッチごとに、スプーフィング可能なx_forwarded_forとより信頼性の高いremote_addrの両方を出力する必要がありました。 if-noneまたはif-singularを他の関数に挿入するための空白を許可する必要がありました(事前にフォーマットされた関数を返すだけではありません)。プラットフォーム設定用にスイッチごとにカスタマイズされたラベルを持つ「オンまたはオフ」変数が必要でした。また、forwarded_forの形式をとるために、リクエストに応じて$ ipを動的にする方法も必要でした。

また、私は誰もisset()対!empty()をアドレスしませんでした-x_forwarded_forに何も入力せずにisset()の真理をトリガーして空のvarを得ることができます、回避する方法は&&を使用して両方を条件として結合することです「PWNED」などの単語をx_forwarded_forとして偽装できることに注意してください。そのため、出力が保護されている場所またはDBに出力する場合は、実際のIP構文に必ず滅菌してください。

また、x_forwarder_forの配列を見るためにマルチプロキシが必要な場合は、Google Translateを使用してテストできます。テストするためにヘッダーを偽装したい場合は、これを確認してください Chrome Client Header Spoof extension。これは、anonプロキシの背後ではデフォルトで標準のremote_addrになります。

Remote_addrを空にすることはできませんが、万が一のためにフォールバックとして使用することはできません。

// proxybuster - attempts to un-hide originating IP if [reverse]proxy provides methods to do so
  $enableProxyBust = true;

if (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR'])) && (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) && (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
    $ip = end(array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']))));
    $ipProxy = $_SERVER['REMOTE_ADDR'];
    $ipProxy_label = ' behind proxy ';
} elseif (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = ' no proxy ';
} elseif (($enableProxyBust == false) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = '';
} else {
    $ip = '';
    $ipProxy = '';
    $ipProxy_label = '';
}

以下の関数またはクエリ/エコー/ビューで使用するためにこれらを動的にするには、ログ生成またはエラー報告などのために、グローバルを使用するか、他の条件や静的スキーマ出力を大量に作成することなく、希望する場所でemをエコーし​​ます機能。

function fooNow() {
    global $ip, $ipProxy, $ipProxy_label;
    // begin this actions such as log, error, query, or report
}

素晴らしいご意見ありがとうございます。これがもっと良いかどうかを教えてください、まだこれらのヘッダーにはちょっと新しいです:)

1
dhaupin

誰かが以前に言ったように、ここで重要なのはユーザーのIPを保存する理由です。

私が取り組んでいる登録システムの例と、もちろん、検索で頻繁に出てくるこの古い議論に貢献するためのソリューションを紹介します。

多くのphp登録ライブラリはipを使用して、ユーザーのIPに基づいて失敗した試行を抑制/ロックアウトします。次の表を検討してください。

-- mysql
DROP TABLE IF EXISTS `attempts`;
CREATE TABLE `attempts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(39) NOT NULL, /*<<=====*/
  `expiredate` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 -- sqlite
...

次に、ユーザーがログインまたはパスワードリセットなどのサービスに関連する何かを実行しようとすると、関数が最初に呼び出されます。

public function isBlocked() {
      /*
       * used one of the above methods to capture user's ip!!!
       */
      $ip = $this->ip;
      // delete attempts from this ip with 'expiredate' in the past
      $this->deleteAttempts($ip, false);
      $query = $this->dbh->prepare("SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ?");
      $query->execute(array($ip));
      $attempts = $query->fetchColumn();
      if ($attempts < intval($this->token->get('attempts_before_verify'))) {
         return "allow";
      }
      if ($attempts < intval($this->token->get('attempts_before_ban'))) {
         return "captcha";
      }
      return "block";
   }

たとえば、$this->token->get('attempts_before_ban') === 10と2人のユーザーが、ヘッダーがスプーフィングされる可能性がある以前のコードの場合と同じipsにアクセスするとします、その後、それぞれ5回試行した後両方が禁止されます!さらに最悪なのは、すべてが同じプロキシからのものである場合、最初の10人のユーザーのみがログに記録され、残りはすべて禁止されます!

ここで重要なのは、テーブルattemptsに一意のインデックスが必要であり、次のような組み合わせから取得できることです。

 `ip` varchar(39) NOT NULL,
 `jwt_load varchar(100) NOT NULL

jwt_loadは、 json Webトークン テクノロジーencryptedペイロードのみを保存するテクノロジーに続くHTTP Cookieから取得されますそのshouldには、すべてのユーザーの任意/一意の値が含まれます。もちろん、リクエストは"SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"に変更する必要があり、クラスはprivate $jwtも開始する必要があります。

1
centurian

単純にIPアドレスを返すのではなく、IP情報を含む配列を返すこの関数を思いつきました。

// Example usage:
$info = ip_info();
if ( $info->proxy ) {
    echo 'Your IP is ' . $info->ip;
} else {
    echo 'Your IP is ' . $info->ip . ' and your proxy is ' . $info->proxy_ip;
}

関数は次のとおりです。

/**
 * Retrieves the best guess of the client's actual IP address.
 * Takes into account numerous HTTP proxy headers due to variations
 * in how different ISPs handle IP addresses in headers between Hops.
 *
 * @since 1.1.3
 *
 * @return object {
 *         IP Address details
 *
 *         string $ip The users IP address (might be spoofed, if $proxy is true)
 *         bool $proxy True, if a proxy was detected
 *         string $proxy_id The proxy-server IP address
 * }
 */
function ip_info() {
    $result = (object) array(
        'ip' => $_SERVER['REMOTE_ADDR'],
        'proxy' => false,
        'proxy_ip' => '',
    );

    /*
     * This code tries to bypass a proxy and get the actual IP address of
     * the visitor behind the proxy.
     * Warning: These values might be spoofed!
     */
    $ip_fields = array(
        'HTTP_CLIENT_IP',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_FORWARDED',
        'HTTP_X_CLUSTER_CLIENT_IP',
        'HTTP_FORWARDED_FOR',
        'HTTP_FORWARDED',
        'REMOTE_ADDR',
    );
    foreach ( $ip_fields as $key ) {
        if ( array_key_exists( $key, $_SERVER ) === true ) {
            foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
                $ip = trim( $ip );

                if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
                    $forwarded = $ip;
                    break 2;
                }
            }
        }
    }

    // If we found a different IP address then REMOTE_ADDR then it's a proxy!
    if ( $forwarded != $result->ip ) {
        $result->proxy = true;
        $result->proxy_ip = $result->ip;
        $result->ip = $forwarded;
    }

    return $result;
}
1
Philipp

ちょうど別のきれいな方法:

  function validateIp($var_ip){
    $ip = trim($var_ip);

    return (!empty($ip) &&
            $ip != '::1' &&
            $ip != '127.0.0.1' &&
            filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
            ? $ip : false;
  }

  function getClientIp() {
    $ip = @$this->validateIp($_SERVER['HTTP_CLIENT_IP']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED']) ?:
          @$this->validateIp($_SERVER['REMOTE_ADDR']) ?:
          'LOCAL OR UNKNOWN ACCESS';

    return $ip;
  }
0
Liko

CloudFlare キャッシングレイヤーサービスを使用する場合の修正バージョン

function getIP()
{
    $fields = array('HTTP_X_FORWARDED_FOR',
                    'REMOTE_ADDR',
                    'HTTP_CF_CONNECTING_IP',
                    'HTTP_X_CLUSTER_CLIENT_IP');

    foreach($fields as $f)
    {
        $tries = $_SERVER[$f];
        if (empty($tries))
            continue;
        $tries = explode(',',$tries);
        foreach($tries as $try)
        {
            $r = filter_var($try,
                            FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 |
                            FILTER_FLAG_NO_PRIV_RANGE |
                            FILTER_FLAG_NO_RES_RANGE);

            if ($r !== false)
            {
                return $try;
            }
        }
    }
    return false;
}
0
jmserra

これをありがとう、非常に便利。

ただし、コードが構文的に正しい場合は役立ちます。行20の周りに{が多すぎます。これは実際に誰も試していないことを意味します。

クレイジーかもしれませんが、いくつかの有効なアドレスと無効なアドレスで試してみたところ、有効なvalidate_ip()の唯一のバージョンは次のとおりでした。

    public function validate_ip($ip)
    {
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false)
            return false;

        return true;
    }
0
Mark Boon

VB.NET 回答のバージョン:

Private Function GetRequestIpAddress() As IPAddress
    Dim serverVariables = HttpContext.Current.Request.ServerVariables
    Dim headersKeysToCheck = {"HTTP_CLIENT_IP", _
                              "HTTP_X_FORWARDED_FOR", _
                              "HTTP_X_FORWARDED", _
                              "HTTP_X_CLUSTER_CLIENT_IP", _
                              "HTTP_FORWARDED_FOR", _
                              "HTTP_FORWARDED", _
                              "REMOTE_ADDR"}
    For Each thisHeaderKey In headersKeysToCheck
        Dim thisValue = serverVariables.Item(thisHeaderKey)
        If thisValue IsNot Nothing Then
            Dim validAddress As IPAddress = Nothing
            If IPAddress.TryParse(thisValue, validAddress) Then
                Return validAddress
            End If
        End If
    Next
    Return Nothing
End Function
0
Abacus

Symfonyのリクエストクラスから https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php

const HEADER_FORWARDED = 'forwarded';
const HEADER_CLIENT_IP = 'client_ip';
const HEADER_CLIENT_Host = 'client_Host';
const HEADER_CLIENT_PROTO = 'client_proto';
const HEADER_CLIENT_PORT = 'client_port';

/**
 * Names for headers that can be trusted when
 * using trusted proxies.
 *
 * The FORWARDED header is the standard as of rfc7239.
 *
 * The other headers are non-standard, but widely used
 * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
 */
protected static $trustedHeaders = array(
    self::HEADER_FORWARDED => 'FORWARDED',
    self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
    self::HEADER_CLIENT_Host => 'X_FORWARDED_Host',
    self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
    self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
);

/**
 * Returns the client IP addresses.
 *
 * In the returned array the most trusted IP address is first, and the
 * least trusted one last. The "real" client IP address is the last one,
 * but this is also the least trusted one. Trusted proxies are stripped.
 *
 * Use this method carefully; you should use getClientIp() instead.
 *
 * @return array The client IP addresses
 *
 * @see getClientIp()
 */
public function getClientIps()
{
    $clientIps = array();
    $ip = $this->server->get('REMOTE_ADDR');
    if (!$this->isFromTrustedProxy()) {
        return array($ip);
    }
    if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
        $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
        preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
        $clientIps = $matches[3];
    } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
        $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
    }
    $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
    $firstTrustedIp = null;
    foreach ($clientIps as $key => $clientIp) {
        // Remove port (unfortunately, it does happen)
        if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
            $clientIps[$key] = $clientIp = $match[1];
        }
        if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
            unset($clientIps[$key]);
        }
        if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
            unset($clientIps[$key]);
            // Fallback to this when the client IP falls into the range of trusted proxies
            if (null ===  $firstTrustedIp) {
                $firstTrustedIp = $clientIp;
            }
        }
    }
    // Now the IP chain contains only untrusted proxies and the client IP
    return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
}
0
luchaninov

私の経験では、ユーザーのIPアドレスはコンマ区切りリストの最後にあるため、展開されたHTTP_X_FORWARDED_FORを逆順に繰り返す必要があるのではないかと思います。返されたプロキシの1つのIPアドレスを取得する可能性が高くなります。これにより、多くのユーザーがそのプロキシを経由する可能性があるため、セッションハイジャックが依然として可能になる可能性があります。

0
Chris Withers