web-dev-qa-db-ja.com

HTTP_X_FORWARDED_FORを適切に使用する方法は?

さて、小さな認証の問題があります。私のWebサービスでは、ユーザー名とパスワードを使用してHTTP経由でAPIに接続できますが、この接続は特定のIPアドレスに制限することもできます。

これは、$_SERVER['REMOTE_ADDR']が正しくない可能性があることを意味します。 IP情報が本当に信頼できるものではないことは既に知っています。別のセキュリティレイヤーを追加しようとする場合にのみ制限があります。

これが私のWebサーバーへのリクエストの一般的な概要である場合:

clientSERVER => clientPROXY => myPROXY => mySERVER

これは、mySERVERがクライアントの代わりにmyPROXYのREMOTE_ADDRを表示し、クライアントの実際のIPをHTTP_X_FORWARDED_FORとして送信することを意味します。

これを克服するために、私のWebサービスには「信頼できるプロキシ」IPアドレスのリストがあり、REMOTE_ADDRがそれらの信頼できるIPアドレスのいずれかからのものである場合、実際のIPアドレスはHTTP_X_FORWARDED_FOR

現在、問題はclientPROXYにあります。これは、mySERVERが複数のIPアドレスを持つHTTP_X_FORWARDED_FOR値を(かなり頻繁に)取得することを意味します。 HTTP_X_FORWARDED_FORドキュメントによると、値はIPアドレスのコンマ区切りリストであり、最初のIPは実際の実際のクライアントのものであり、他のすべてのIPアドレスはプロキシのものです。

したがって、HTTP_X_FORWARDED_FORに複数の値があり、サービスがIP制限されている場合、許可されたIPリストに対してHTTP_X_FORWARDED_FORの「最後の」値を確認し、実際のクライアントIPを無視する必要がありますか?

許可されたIPアドレスのリストを設定する必要があるシステムでは、ホワイトリストに登録されたIPアドレスは、プロキシの背後にあるIPではなく、プロキシのIPアドレスである必要があります(ローカルホストIPで頻繁に変更される可能性があるため) 。

HTTP_CLIENT_IPはどうですか?

34
kingmaple

この関数を使用して、適切なクライアントIPを取得できます。

public function getClientIP(){       
     if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)){
            return  $_SERVER["HTTP_X_FORWARDED_FOR"];  
     }else if (array_key_exists('REMOTE_ADDR', $_SERVER)) { 
            return $_SERVER["REMOTE_ADDR"]; 
     }else if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) {
            return $_SERVER["HTTP_CLIENT_IP"]; 
     } 

     return '';
}
25

Hrishikeshの答えが好きです。これに追加するだけです...途中で複数のプロキシが使用されたときにカンマ区切りの文字列が出てきたので、爆発を追加して最終値を取得する必要があることがわかりましたこの:

$IParray=array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR'])));
return end($IParray);

空のエントリを削除するためにarray_filterがあります。

21
user336828

最新の httpoxy の脆弱性に照らして、HTTP_X_FORWARDED_FORを適切に使用する方法の完全な例が本当に必要です。

クライアントがプロキシの背後にある可能性があり、このプロキシを信頼できることがわかっている場合、クライアントIPアドレスを検出する方法をPHPで記述した例を次に示します。信頼できるプロキシがわからない場合は、REMOTE_ADDRを使用します

<?php

function get_client_ip ()
{
    // Nothing to do without any reliable information
    if (!isset ($_SERVER['REMOTE_ADDR'])) {
        return NULL;
    }

    // Header that is used by the trusted proxy to refer to
    // the original IP
    $proxy_header = "HTTP_X_FORWARDED_FOR";

    // List of all the proxies that are known to handle 'proxy_header'
    // in known, safe manner
    $trusted_proxies = array ("2001:db8::1", "192.168.50.1");

    if (in_array ($_SERVER['REMOTE_ADDR'], $trusted_proxies)) {

        // Get the IP address of the client behind trusted proxy
        if (array_key_exists ($proxy_header, $_SERVER)) {

            // Header can contain multiple IP-s of proxies that are passed through.
            // Only the IP added by the last proxy (last IP in the list) can be trusted.
            $proxy_list = explode (",", $_SERVER[$proxy_header]);
            $client_ip = trim (end ($proxy_list));

            // Validate just in case
            if (filter_var ($client_ip, FILTER_VALIDATE_IP)) {
                return $client_ip;
            } else {
                // Validation failed - beat the guy who configured the proxy or
                // the guy who created the trusted proxy list?
                // TODO: some error handling to notify about the need of punishment
            }
        }
    }

    // In all other cases, REMOTE_ADDR is the ONLY IP we can trust.
    return $_SERVER['REMOTE_ADDR'];
}

print get_client_ip ();

?>
13
Erki A

また、 mod_remoteip を使用してApache構成でこの問題を解決するには、conf.dファイルに次を追加します。

RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 172.16.0.0/12
LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
2
zeroimpl

データベースで使用する場合、これは良い方法です。

データベースのipフィールドをvarchar(250)に設定し、これを使用します。

$theip = $_SERVER["REMOTE_ADDR"];

if (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
    $theip .= '('.$_SERVER["HTTP_X_FORWARDED_FOR"].')';
}

if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
    $theip .= '('.$_SERVER["HTTP_CLIENT_IP"].')';
}

$realip = substr($theip, 0, 250);

次に、データベースのIPフィールドに対して$ realipをチェックするだけです

0
mowgli