IP アドレスが偽装可能か確認してみよう

PHP フレームワークでのクライアント IP アドレス取得メソッドの実装について で、CakePHP, CodeIgniter, Symfony, Zend Framework でのクライアント IP アドレス取得メソッドについてみてみました。

今回は、実際に自分のサーバで IP アドレスが偽装可能か確認してみましょう。

まず、サーバに検証用のソースを置きます。

CodeIgniter だと、こんなコードになります。

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Ip_address extends CI_Controller {

	function __construct()
	{
		parent::__construct();
	}

	function index()
	{
		echo $this->input->ip_address();
	}
}

/* End of file ip_address.php */
/* Location: ./application/controllers/ip_address.php */

イムリー?にも、プロキシ経由でもリアルIPを取得するPHPコードスニペット:phpspot開発日誌 という記事がありました。そこで紹介されているコードは以下のようなものでした。

<?php

if (!empty($_SERVER["HTTP_CLIENT_IP"]))
{
  //check for ip from share internet
  $ip = $_SERVER["HTTP_CLIENT_IP"];
}
elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
{
  // Check for the Proxy User
  $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
}
else
{
  $ip = $_SERVER["REMOTE_ADDR"];
}

// This will print user's real IP Address
// does't matter if user using proxy or not.
echo $ip;

ここでは、上記のファイルを show_ip.php という名前にして、サーバに転送することにします。

次に、テスト用のコードを書きましょう。

サーバに

  1. 追加ヘッダなし
  2. X-FORWARDED-FOR ヘッダ付き
  3. CLIENT-IP ヘッダ付き
  4. X-FORWARDED-FOR および CLIENT-IP ヘッダ付き

のリクエストを送るコードです。

test.php:

<?php

$uri = 'http://www.example.jp/show_ip.php';

// no additional headers
$page = file_get_contents($uri);
echo 'No headers:      ';
echo $page, "\n";

// send X-FORWARDED-FOR
$opt = array(
             'http'=> array(
                            'header' => 'X-FORWARDED-FOR: 88.88.88.88',
                      )
       );
$context = stream_context_create($opt);
$page = file_get_contents($uri, 0, $context);
echo 'X-FORWARDED-FOR: ';
echo $page, "\n";

// send CLIENT-IP
$opt = array(
             'http'=> array(
                            'header' => 'CLIENT-IP: 99.99.99.99',
                      )
       );
$context = stream_context_create($opt);
$page = file_get_contents($uri, 0, $context);
echo 'CLIENT-IP:       ';
echo $page, "\n";

// send X-FORWARDED-FOR and CLIENT-IP
$opt = array(
             'http'=> array(
                            'header' => array('X-FORWARDED-FOR: 88.88.88.88',
                                              'CLIENT-IP: 99.99.99.99',
                                        )
                      )
       );
$context = stream_context_create($opt);
$page = file_get_contents($uri, 0, $context);
echo 'Both:            ';
echo $page, "\n";

さあ、このコードを手許の PC から実行してみましょう。

例えば、こんな結果になりました。

この環境では、リバースプロキシがあり CLIENT-IP ヘッダは通りませんが、X-FORWARDED-FOR は通るようです。

そもそも、カンマ区切りで IP アドレスが複数ありますね。これをそのまま IP アドレスだとして処理したらまずいことになりますね。