こんにちは、亀本です。
ちょうど今日、予約していたCakePHPガイドブックが会社に届きました。
CakePHPはチュートリアルのサンプルを作ったり、その時に中をぼんやり眺める程度しか触っていなかったので、この機会にちょっと勉強してみようかなと思っています。
1つのフレームワーク本としては厚みがあり、著者陣も「日本でCakePHPといったら」という方々なので期待大です。
ついでに、あわせてsymfony本も読んでやってください、とか宣伝もしておきます(^^;。
さて、そんな話を振っておいて今回は全然違うIP制限のお話です。
携帯対応のサイトになると、できればPCからのアクセスと携帯端末からのアクセスを振り分けたい、という要求が出てきたりします。
USER_AGENTで振り分ける方法もありますが、これは偽装が極めて容易なので振り分け条件としては不十分です。
そこで多くの場合、各キャリアの発表しているIP帯域で振り分けをするという方法が取られます。
もちろん、IPで絞っても偽装されてしまうことはありますが、ちょっとしたお遊びレベルでの不正アクセスは防げます。
今回は、そんな「特定のIP帯域で振り分ける」という方法を自分の知る範囲内でまとめてみました。
# とはいえ自分も詳しい方ではないので、他にもっといい方法あるだろー!これはだめだろー!という話があればぜひ教えてください。
1 .htaccessによるアクセス権限設定で振り分ける
最初は、.htaccessでアクセスを許容するIP帯域を設定して、拒否されたものをリダイレクトするという方法です。
Order deny,allow
Deny from All
# 許可するIP帯域を羅列
Allow from xxx.xxx.xxx.xxx/xx
Allow from xxx.xxx.xxx.xxx/xx
・
・
・
ErrorDocument 403 http://.....
多分、Apacheを利用する場合には一番手軽な方法です。管理も楽です。
ただ、「振り分け」という観点からすると、バッドノウハウの部類になるような気もします。
他の理由に起因するアクセス制限も同時にかけたい場合(管理側へのアクセスとか)に不便なんじゃ、とか思う点もあったりして、業務で使うとなると、個人的には微妙です。
個人でサイト作っていて色々考えるのが面倒だったりする場合には、これが一番手っ取り早いかな、という感じです。
2 mod_rewriteを利用する
次はApacheのmod_rewriteを利用する方法です。
RewriteEngin On
RewriteCond %{REMOTE_ADDR} !xxx\.xxx\.xxx\.* [OR]
RewriteCond %{REMOTE_ADDR} !xxx\.xxx\.xxx\.xxx [OR]
・
・
・
RewriteRule ^.*$ http://.... [L]
とすることで、特定のIP帯域以外の場合にRwriteRuleで指定したURLへリダイレクトするようにします。
合わせてHTTP_USER_AGENTなども見てやれるので、柔軟性も高くて便利です。
これを、携帯サイトのIP帯域による振り分けに使用する上で問題があるとすれば、帯域が中途半端な場合(xxx.xxx.xxx.xxx/26とか)には記述がかなり面倒になるという点でしょうか。
自分は、正規表現が目視で間違ってるとわからない方のPHPerなので、各キャリアから発表されているIP帯域一覧に正しく対応する正規表現を、何行も何行もサクサク書いていったりできないし、だからと言って個別に書いてたら発狂します。
携帯の振り分けに使うのであれば、DNSを逆引きしてREMOTE_HOSTを利用、というのがこの方法の現実的な使い方になるんじゃないでしょうか。なおその場合、それなりのアクセス数を稼ぐサイトであれば、逆引きの負荷に注意する必要があります。
# IP振り分けの話をしていて逆引きか。。。とか言わないでくださいw
mod_rewrite: http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html
参考: http://www.net-newbie.com/trans/mod_rewrite.html
3 PEAR::Net_IPv4を使う
やっぱりPHPerなので、こういうとき頼るのはPEARライブラリです。
Net_IPv4ライブラリを使うと、IP帯域による振り分けがアプリケーション側で容易に実装できます。
インストールはいつもの如く
$ pear install Net_IPv4
このNet_IPv4ライブラリのipInNetwork()メソッドを利用すると、指定したIP帯域内に含まれているかの判定が可能です。
以下が簡単なサンプルです。
<?php
require_once("Net/IPv4.php");
// $ip_table_listに xxx.xxx.xxx.xxx/24 などのIP帯域の配列を格納
$is_mobile_ip = false;
foreach ($ip_table_list as $value) {
if (Net_IPv4::ipInNetwork($_SERVER["REMOTE_ADDR"], $value)) {
$is_mobile_ip = true;
break;
}
}
if (!$is_mobile_ip) {
header("Location: http://...");
}
この方法の欠点は、スクリプトで処理するために上記2つの方ほよりずっと負荷が高くなりやすい点でしょう。リストが多ければ多いほどそれは顕著になります。
ただ、振り分けに使用する場合には相応の利点があります。
まず、複雑な処理をした上での振り分けが可能ですし、そもそもスクリプト内なのでリダイレクトせずにそのまま別処理に渡せます。
# 上記例ではリダイレクトしていますが。
ですので、リダイレクトにかかる分の負荷やユーザ側の体感速度を考えると、こちらの方がリーズナブルな場合も多いのではないでしょうか。
サーバ設定とは切り離されるし、一枚物のスクリプトでPEAR.phpしかincludeしないので、共用サーバで利用する上でも便利です。
Net_IPv4: http://pear.php.net/package/Net_IPv4/
ドキュメント: http://pear.php.net/manual/ja/package.networking.net-ipv4.php
恐らく、使用する上ではどれも一長一短なところがあり、パーフェクトな回答は難しい気がします。
状況に応じてどの方法をとるか、検討する上での一助になれば幸いです。
参考:各携帯キャリアのIPアドレス帯域
DoCoMo: http://www.nttdocomo.co.jp/service/imode/make/content/ip/
au: http://www.au.kddi.com/ezfactory/tec/spec/ezsava_ip.html
SoftBank: http://developers.softbankmobile.co.jp/dp/tech_svc/web/ip.php
Willcom: http://www.willcom-inc.com/ja/service/contents_service/club_air_edge/for_phone/ip/