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


301

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

私は時間をかけて詳細なソリューションを見つけようとして、いくつかのソースに基づいて次のコードを考え出しました。誰かが答えに穴を開けたり、より正確なものに光を当てたりできたら、私はそれを気に入っています。

編集には@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、プロキシの背後にいる人にケータリングすることを頼りにしないでください。


2
whatismyip.comの質問について、私は彼らがこのスクリプトのようなことをしていると思います、それをローカルで実行していますか?それが内部IPを使用している理由である場合、その場合はパブリックインターフェイス経由で何も送信されないため、phpが取得する情報はありません
Matt

2
確かにこれを実装するときは、このことを覚えておいてください:stackoverflow.com/questions/1672827/...
ケビンPeno

19
これらのHTTPヘッダーはすべて変更が非常に簡単であることを覚えておいてください。ソリューションでは、ランダムなIPを使用してX-Forwarded-Forヘッダーを送信するようにブラウザーを構成するだけで、スクリプトは喜んで偽のアドレスを返します。したがって、何をしようとしているのかによっては、このソリューションは単にREMOTE_ADDRを使用するよりも信頼性が低くなる可能性があります。
gnomnain 2010年

14
OMFG、「信頼できないIP」!初めてここでそのようなナンセンスをSOで目にします。信頼できる唯一のIPアドレスはREMOTE_ADDRです
常識

3
-1これは、なりすましに対して脆弱です。あなたがしているすべては彼のIPアドレスがどうあるべきかユーザーに尋ねることです。
ルーク

回答:


269

以下は、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アドレスを検証することもできます。


4
このfilter_var修正により、IPアドレスに対するハッキングされた未署名の整数チェックが削除されるため、間違いなく感謝します。また、IPv6アドレスを検証するオプションも提供されている点も気に入っています。HTTP_X_FORWARDED_FOR最適化もはるかに高く評価されます。数分でコードを更新します。
Corey Ballou、2010年

33
-1これはあなたがしているすべてのなりすましに対して脆弱であり、ユーザーに自分のIPアドレスを尋ねるだけです。
ルーク

7
@ルーク:はい、知っています。OPはそれを認識しており、私の回答でも触れました。しかしコメントをありがとう。
Alix Axel 2011年

1
参考:Alix Axelのコードを機能させるには、FILTER_FLAG_IPV6を削除する必要がありました。
darkAsPitch 2009

2
@ rubenrp81 TCPソケットハンドラーは唯一の正規のソースであり、その他はすべて攻撃者が制御します。上記のコードは攻撃者の夢です。
2016

12

ただし、それでもユーザーの実際のIPアドレスを取得することは信頼できなくなります。匿名のプロキシサーバー(http_x_forwarded_for、http_forwardedなどのヘッダーを受け入れないサーバー)を使用するだけで、プロキシサーバーのIPアドレスを取得するだけです。

これで、匿名のプロキシサーバーのIPアドレスのリストがあるかどうかを確認できますが、これが100%正確であることを確認する方法はありません。そのほとんどは、プロキシサーバーであることを通知することです。そして、誰かが賢い場合、HTTP転送のヘッダーを偽装することができます。

私は地元の大学が好きではないとしましょう。私は彼らが登録したIPアドレスを見つけ、悪いことをすることであなたのサイトで彼らのIPアドレスが禁止されるようにしました。リストは無限大です。

次に、ご想像のとおり、以前に言及した大学のネットワークなどの内部IPアドレスがあります。多くは10.xxx形式を使用しています。つまり、共有ネットワークに転送されたということだけです。

それから私はそれから多くを始めることはしませんが、ダイナミックIPアドレスはもはやブロードバンドの方法です。そう。ユーザーIPアドレスを取得した場合でも、長くても2〜3か月で変更されることが予想されます。


入力いただきありがとうございます。私は現在、ユーザーのIPアドレスを利用して、セッションハイジャックを制限する制限要因としてクラスC IPを使用することにより、セッション認証を支援していますが、適切な範囲内で動的IPを許可しています。なりすましIPと匿名プロキシサーバーは、特定の個人グループのために対処しなければならないものです。
Corey Ballou、

@cballou-確かにこの目的のためには、REMOTE_ADDRを使用するのが適切です。HTTPヘッダーに依存するアプローチは、ヘッダーのなりすましに対して脆弱です。セッションの長さは?動的IPは速く変化しません。
MZB 2010年

それらは、特に私がそれらを望んでいる場合(多くのドライバーがサポートしているMACアドレスを変更する)、そうします。REMOTE_ADDRだけで、最後に通信したサーバーを取得できます。したがって、プロキシの状況では、プロキシIPを取得します。

8

を使用しております:

/**
 * 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アドレスを検出していた奇妙な問題が原因です。


おっと、私はあなたが基本的に同じことをで爆発することなどに気づきました。プラス少し余分。だから私は私の答えが多くの助けであったとは思いません。:)
ガブリエルク2010年

これにより、ローカルホストのアドレスが返されます
Scarl

3

私の答えは基本的に、@ 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)行のコードだけです。


2

最大の質問は、どのような目的ですか?

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

確かに、いかなる種類の認証にもIPアドレスを使用してはなりません。これらは偽装される可能性もあります。

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

C.


PHPのみのソリューションを探していることを考慮し$_SERVER['CLIENT_IP']て、2番目のelse ifステートメントとして追加することを提案していますか?
Corey Ballou 2010年

いいえ-返されるデータに重要性を持たせたい場合は、ネットワークエンドポイントアドレス(クライアントIP)と、プロキシが追加したヘッダーに異なる値を示唆するもの(たとえば、 192.168.1.xのアドレスの多くが、異なるクライアントIPから来る)C.見る
symcbean

1

上記の回答の方がはるかに簡潔でわかりやすく、これは関数ではなく、最も優雅なスクリプトでもありません。今回のケースでは、単純化されたスイッチごとに、なりすまし可能な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クライアントヘッダースプーフ拡張機能をご覧ください。これはデフォルトで、非プロキシの背後にある標準の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

単に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

誰かが以前に言ったように、ここでの鍵は、どのような理由でユーザーの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人のユーザーが同じIPにアクセスし、ヘッダーを偽装できる場合、5回試行すると両方が禁止されますます。最悪の場合でも、すべてが同じプロキシからのものである場合、最初の10人のユーザーのみがログに記録され、残りはすべて禁止されます。

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

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

どこjwt_load次のHTTPクッキーから来ているJSONウェブトークン我々は唯一の保存技術、暗号化ペイロードなければならないすべてのユーザーのための任意/一意の値が含まれています。もちろん、リクエストは次のように変更する必要が"SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"ありprivate $jwtます。クラスもを開始する必要があります。


0

私の経験では、ユーザーのIPアドレスはコンマ区切りのリストの最後で終わり、ヘッダーの先頭から始まっているため、逆の順序で展開されたHTTP_X_FORWARDED_FORを反復する必要があるのではないかと思います。返されたプロキシのいずれかのIPアドレスを取得する可能性が高く、多くのユーザーがそのプロキシを経由する可能性があるため、セッションの乗っ取りを引き続き許可する可能性があります。


1
HTTP_X_FORWARDED_FOR:en.wikipedia.org/wiki/X-Forwarded-Forのウィキペディアページを読んだ ... コードのとおり 、提案された順序は左から右です。しかし、私たちのログから、これが実際にはプロキシによって尊重されていない多くのケースがあり、チェックしたいIPアドレスがリストの両端にあることがわかります。
Chris Withers、2010年

1
または、途中で、一部のプロキシが左から右への順序を尊重し、他のプロキシが順守しなかった場合に発生するように。
ブリリアント、

0

これをありがとう、とても便利です。

ただし、コードが構文的に正しい場合に役立ちます。{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

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

答えの単なる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

3
質問にはタグ「PHP」があります
luchaninov

0

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

  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

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);
}

未定義のプロパティ:$ server
C47

0

filter_inputについて誰も言及していないことに驚いているので、ここにAlix Axelの回答を1行に要約します。

function get_ip_address(&$keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'])
{
    return empty($keys) || ($ip = filter_input(INPUT_SERVER, array_pop($keys), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))? $ip : get_ip_address($keys);
}

-1

あなたはほとんどあなた自身の質問に答えました!:)

function getRealIpAddr() {
    if(!empty($_SERVER['HTTP_CLIENT_IP']))   //Check IP address from shared Internet
    {
        $IPaddress = $_SERVER['HTTP_CLIENT_IP'];
    }
    elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))   //To check IP address is passed from the proxy
    {
        $IPaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
    else
    {
        $IPaddress = $_SERVER['REMOTE_ADDR'];
    }
    return $IPaddress;
}

ソース


-6
/**
 * Sanitizes IPv4 address according to Ilia Alshanetsky's book
 * "php|architect?s Guide to PHP Security", chapter 2, page 67.
 *
 * @param string $ip An IPv4 address
 */
public static function sanitizeIpAddress($ip = '')
{
if ($ip == '')
    {
    $rtnStr = '0.0.0.0';
    }
else
    {
    $rtnStr = long2ip(ip2long($ip));
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized HTTP_X_FORWARDED_FOR server variable.
 *
 */
public static function getXForwardedFor()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
elseif (isset($HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'];
    }
elseif (getenv('HTTP_X_FORWARDED_FOR'))
    {
    $rtnStr = getenv('HTTP_X_FORWARDED_FOR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized REMOTE_ADDR server variable.
 *
 */
public static function getRemoteAddr()
{
if (isset($_SERVER['REMOTE_ADDR']))
    {
    $rtnStr = $_SERVER['REMOTE_ADDR'];
    }
elseif (isset($HTTP_SERVER_VARS['REMOTE_ADDR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['REMOTE_ADDR'];
    }
elseif (getenv('REMOTE_ADDR'))
    {
    $rtnStr = getenv('REMOTE_ADDR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized remote user and proxy IP addresses.
 *
 */
public static function getIpAndProxy()
{
$xForwarded = self::getXForwardedFor();
$remoteAddr = self::getRemoteAddr();

if ($xForwarded != '')
    {
    $ip    = $xForwarded;
    $proxy = $remoteAddr;
    }
else
    {
    $ip    = $remoteAddr;
    $proxy = '';
    }

return array($ip, $proxy);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.