SymfonyでIP制限するフィルター作りました

こんにちは。カヤックモバイル$アラガです。

諸事情により.htaccessによるIP制限ができなかったので、symfonyでIP制限するフィルタを作りました。

携帯サイトのキャリア判別は、おなじみのNet_UserAgent_Mobileを使うと簡単ですが、ユーザーエージェントだけでは簡単に偽装できてしまうので、公式サイト等の場合は望ましくないです。

そこで、各キャリアから公開されているIPでも制限をかけます。.htaccessで良ければMobile IP htaccess Makerを使うのが簡単です。 しかし、リバースプロキシを使っていると、IPが経由サーバーのIPになってしまい、元のIPがX-Forwarded-Forに入ってしまいこの方法だとうまくいきません。(ということに気づきました) そこで制限したいIPを正規表現で書いて SetEnvIf X-Forwarded-For "^192\.168\.1\." allow_ip というのを大量に書くのもありですが、 正規表現にもサブネットマスクの計算にも疲れちゃうので、.htaccessはあきらめてsymfonyのFilterを作りました。

前置きが長くなりましたが、以下紹介します。

IPFilter.php

<?php
class IPFilter extends sfFilter
{
    public function execute($filterChain = null)
    {
        if ($this->isFirstCall())
        {
            $ip_conf = array();     
            $access_ip = "";

            if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                $access_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
            }
            else if (isset($_SERVER['REMOTE_ADDR'])) {
                $access_ip = $_SERVER['REMOTE_ADDR'];
            }

            $name = "ip_conf_";
            if (defined("MOBILE_CARRIER")) $name.= MOBILE_CARRIER;

            $name .= ".php";

            $app_dir = SF_ROOT_DIR.DIRECTORY_SEPARATOR.SF_APP
                                    .DIRECTORY_SEPARATOR."config";
            $root_dir = SF_ROOT_DIR.DIRECTORY_SEPARATOR."config";

            if (file_exists($app_dir."/".SF_ENVIRONMENT."_".$name)) {
                include $app_dir."/".SF_ENVIRONMENT."_".$name;
            }
            else if (file_exists($app_dir."/".$name)) {
                include $app_dir."/".$name;
            }
            else if (file_exists($root_dir."/".SF_ENVIRONMENT."_".$name)) {
                include $root_dir."/".SF_ENVIRONMENT."_".$name;
            }
            else if (file_exists($root_dir."/".$name)) {
                include $root_dir."/".$name;
            }

            if (sfConfig::get('ip_conf') != "") {
                $ip_conf = sfConfig::get('ip_conf');
            }

            $result = true;

            foreach ($ip_conf as $order => $list) {
                $match_type = ($order == "deny") ? false : true;
                if (!is_array($list)) {
                    $result = $match_type;
                    continue;
                }
                foreach ($list as $ip) {
                    if ($ip == "*"
                        || $this->check_ip($ip, $access_ip)) {
                        $result = $match_type;
                        break;
                    }
                }
            }

            if (!$result) {
                if (($url = sfConfig::get('app_403_redirect')) == "") {
                    $url = '/403.php';
                }
                header('Location: '.$url);
                    exit();
            }
        }

        if ($filterChain)
            $filterChain->execute();
    }

    protected function check_ip($range, $ip){
        $ip=ip2long($ip);
        if(strchr($range,"/")){ 
            // サブネットマスク
            list($address,$len)=split("/", $range);
            $address=ip2long($address);
            $address=($address >> (32-$len)) << (32-$len);
            return ($address == ($ip & $address) ) ? true : false;
        } 
        else if(strchr($range,"-")){ 
            // 範囲指定
            $range=split("-",$range);
            $range[0]=ip2long($range[0]);
            $range[1]=ip2long($range[1]);
            return ($range[0]<=$ip && $ip <= $range[1] )? true : false;
        }
        return (ip2long($range) == $ip) ? true : false;
    }

    private function log($message) {
        if (sfConfig::get('sf_logging_enabled'))
        {
            $this->getContext()->getLogger()->info("{IPFilter} ".$message);
        }
    }
}
?>

MOBILE_CARRIERについては、後日詳しく説明しおうと思いますが、symfonyのフロントコントローラーをキャリアごとに分けてそこで設定しています。 リダイレクトしているところは好きに直した方がいいかもしれません

設定ファイル

root/config/ip_config_im.php (ドコモ用設定ファイル)

<?php
//Order deny,allow',
//deny from all',
$ip_conf = array (
    'deny' => "*",
    'allow' => array (
        #docomo 2008/09/25
        '210.153.84.0/24',
        '210.136.161.0/24',
        '210.153.86.0/24',
        '124.146.174.0/24',
        '124.146.175.0/24',
        '210.153.87.0/24',
        '203.138.180.0/24',
        '203.138.181.0/24',
        '203.138.203.0/24',

        #Google 2008/09/25
        '72.14.199.0/25',
        '209.85.238.0/25',

        #KAYAC Office
        'XXX.XXX.XXX.XXX',
    )
);
?>

このファイルはどっかから拾ってきた.htaccessを置換すれば簡単に作れます。 ファイル名に_imは僕の場合ですので、いい感じに変更してください。 例えば dev_ip_config.phpだと開発環境のみに適用されます

使い方

1.アプリケーションのfilters.ymlに以下を追加します

ip:
 class: IPFilter

2.symfony cc

  1. 403.phpを作る。(自分に合わせてカスタマイズして下さい)

あー、簡単ですね。

カヤックでは携帯サイトのノウハウにたいして造詣の深い方や、日本一面白い携帯サイトを作る情熱のあるプログラマを募集しています!