SOARISTO工房 Logo
Software Archive
2012/09/20

 前回のフォローです。

 中国、韓国、その他からのアクセスを、“片っ端から遮断して”気を良くしていたところ、特定のIPアドレス空間からのアクセスが、遮断できていないことが分かりました。

 以前にも出てきた、

   *.dynamic.163data.com.cn

からのものです。

 ダダ漏れでした。0xF9FC

 結論からいうと、IPアドレス空間から、CIDRを求める関数に問題がありました。

 前回は、IPの開始アドレスと終了アドレスから、ちょ~イイ加減な方法でCIDRを算出していました。また、PHPのオンラインマニュアルにある例題の関数「ip2cidr()」にも、問題があることか分かりました。

 いろいろ試行錯誤して分かったことは、「Whoisコマンドを使ってAPNICから得られるIPアドレス空間には、複数のCIDRブロックで構成されているものがある」ということでした。

 ということで、さっそく改修です。

 上記は、PHPスクリプトの例です。

 冒頭の“ダダ漏れリスト”の「218.86.48.48」について、APNICにてIPアドレス空間(inetnum)を求めると、「218.85.0.0 - 218.86.127.255」と出てきます。

 これを、上記の関数(1)「ちょ~イイ加減な方法」と、関数(2)「PHPのオンラインマニュアルにある例題」に掛けると、CIDRは、「218.85.0.0/15」と出てきます。

 32-15=12bit分のアドレス空間が遮断されるので、一見、合ってそうに思えますが、これでは「218.86.*.*」のアドレス空間が、遮断されていないことになります。

   218.85.0.0/16
   218.86.0.0/17

 正しくは、上記の2つのCIDRブロックを遮断しなければなりません。

 ということで、IPアドレス空間から、CIDRを求める正しい関数が、関数(3)「range2cidrlist()」です。

 基本的には、PHPのオンラインマニュアルにある例題の関数と同じですが、微妙に修正してあります。(ネットマスクが32だった場合に、正しく計算されないバグを修正)

 この関数は、IPアドレスの範囲(開始アドレスと終了アドレス)を与えると、その範囲に含まれるCIDRブロックを、リスト(配列)として返します。

#正しく計算されているかどうかは、ここなどを参照。

 この返り値を元に、スパムテーブルと.htaccessとを更新し、なんとか“漏れ”を止めることができました。

 ふ~っ、めでたし、めでたし。0xF9C6

spam01.jpg

 それにしても、相変わらず中国からのアクセスが多いです。全体の3分の2近くを占めています。

 引き続き、片っ端から遮断していくことにします。0xF9D1

2012/09/17

 <frame>による画面構成を修正し、アクセスログの集計パターンが変わったところ、捨て置きならない事態が判明しました。

 上記は、アクセスログの一部を集計したものですが、昨日の総アクセス10,228回のうち、中国からのアクセスが6,683回と、ほぼ7割近くを占めていることが分かりました。
(今朝も9:00の時点で、すでに2,500を超えています)

 これを、映画「マトリックス」のワンシーンに喩えると・・・、

matrix01.jpg
(画像は、ワーナー・ブラザーズ・エンターテインメントさんから拝借)

 巨大な多足動物(節足動物)のようなワームが、付け入る先を探して、あちこちからウジャウジャ押し寄せてきているようなイメージ。0xF9FC

 これまでは、「Movable Type」がスパム認定したコメント(またはトラックバック)のIPアドレスのみアクセス規制していましたが(記事1, 記事2, 記事3)、これを機に、より厳格な対処をすることにしました。

 IPアドレスから国名コードを割り出し、中国からのアクセスであった場合には、即座にアクセスを遮断します。

 ついでに、中国(CN)の他に、北朝鮮(KP)、韓国(KR)、香港(HK)、台湾(TW)、ロシア(RU)も規制対象としました。

 規制する対象は、アクセスのあったIPアドレスのみならず、そのIPアドレスが含まれるサブネット全体を、丸ごと規制します。

#なお、IPアドレスの抽出は、proxy経由のものにも対応しているため、どこかを踏み台にしてアクセスしてきても、大丈夫なようになっています。0xF9D1

 上記は、PHPスクリプト(の一部)です。

 関数GetCountryCode()にて、IPアドレスから国名コードを得ています。

 以前の記事では、IPアドレスから国名コードを得る関数として、内部でwhoisコマンドを実行し、その出力から国名コードを得るものを使っていましたが、実行速度を測ってみると、約800msも掛かっていることが分かりました。

 「Movable Type」が認定したスパムテーブルを元に、1日に1回、規制リストを更新する分には、この程度の速度でも構わないのですが、今回のように、即座に規制を掛けるためには、関数自体を高速化する必要があります。

 ネット上をいろいろ探したところ、GeoIPというPHPライブラリの中に、geoip_country_code_by_addr()という関数があったため、このライブラリを組み込んで使うことにしました。

 whoisのように、常に最新のデータベースに聞きにいく訳ではなく、規定の(ローカルな)データベースをルックアップするだけなのですが、実行速度が約200μs前後(実測参考値)と高速で、これならアクセスが頻発しても十分に使えそうです。

 GetCountryCode()にて国名コードを得て、これが中国・香港・台湾、北朝鮮・韓国、ロシアであった場合には、スパムテーブルと.htaccessとを、即座に更新します。

 ワームが入ろうとした瞬間、シェルターの防護壁が、一瞬にして(しかも次々と)閉じられていくようなイメージ。0xF9C5

0xF8D8(ガシャン、ガシャン、ガシャ~ン!!)

2012/09/15

 工房blogの中身を刷新しました。

blog01.jpg

 といっても、見掛けはほとんど変わっていませんが。0xF9C7

 2001年7月の開設以来、画面構成に<frame>を使ってきました。

 この<frame>、SEO対策的には“あまりよろしくない”らしいのですが、そう言われると反抗したくなる質(たち)なので、これまでいろいろ内部的に工夫してやってきました。

 あえて<frame>を使ってきたのは、どんなに画面がスクロールしても、どんなに深い階層に入っても、左上のルートメニューだけは固定位置にあり、一発で希望のメニューに戻ることができるためです。
(現在のところ、ユーザビリティという点では、これに勝る画面構成はないと思います)

 んがしかし、先に購入したXperia GX(Android 4.0)の標準ブラウザでは、正しく表示されないことが分かりました。(Xperia acro(Android 2.3)ではOK)

 ということで、工房blogの中身を、全面的に刷新することにしました。

 CSS(Cascading Style Sheets)を駆使して、疑似的にフレームを構成するとともに、PHPスクリプトも新しく書き直しました。

2012/08/04

 前回の続きです。

 $SiteFileNameを指定してExtractSiteLinks()を呼び出すと、サイトマップ情報(sitemaps.xml)からリンクを抽出し、配列($SiteLinks)として返します。

 あとは、配列の一つ一つの要素に対してCompressFile()を掛ければ、できあがりです。

 13行目で、XMLの<loc>~</loc>タグで挟まれた文字列を抽出し、リンク情報としています。

 15行目では、リンク情報を、URL表記からサーバ上の絶対パスに変換しています。14行目では、リンク情報が“/”で終わっているものについて、“/index.php”を追加して実体化しています。

 実行結果です。

 前回の処理以降、更新されているファイルのみ圧縮してくれます。blogをアップした時に手動で実行するか、あるいはcrontabを使って夜中に自動で実行させても良いかも知れません。

#とりあえず動かすことだけを先行したので、サイトマップ情報等の変数は直値で指定していますが、引数を与えて汎用にするか等は、お好みで。

2012/08/01

 久しぶりに、PHPのスクリプトを書いてみました。

 標記のとおり、MTが吐き出したPHPコードなどを圧縮して、ダウンロードを高速化するためのものです。

 実現する機能の割には、ライン数が多いように見えますが、特に複雑なことはしていません。

   $FileName = "/home/(username)/blog/index.php";
   CompressFile($FileName);

 などとしてCompressFile()を呼び出すと、$FileNameで指定したファイルの中身を解析して、圧縮してくれます。

 成功した場合には、圧縮したファイルのサイズ(byte数)を返し、何らかの問題で失敗した場合には、FALSEを返します(または強制終了)。

 中心となるのは、21~27行目です。

 21行目では、「/*_~_*/」で囲まれた(複数行にわたる)文字列をコメントと見なし、削除しています。同様に、22~23行目では、「//_~」および「#_~」以降の文字列をコメントと見なし、削除しています。(空白文字(スペース)は、“_”と表記)

#正確には、ダブルクォーテーションやシングルクォーテーションで囲まれた「//」や「#」は除外しなければなりませんが、スクリプトがややしこくなるので、今回は省略しました。

 25行目は、連続するタブ文字または改行文字を、1文字のスペースに変換しています。26行目は、連続するスペースを、1文字のスペースに変換しています。

 27行目は、「<p>~</p>_<p>~</p>」など、タグの間に挟まれたスペースを削除しています。

 もう少し頭をひねれば、mb_ereg_replace()の中の正規表現を、よりスマートなものにできると思いますが、いくつかのパターンで試してみたところ、上記のパターンが最もシンプルなものとなりました。

 なお、正規表現は同じでも、その順番を入れ換えてしまうと、正しく動作しません。

 MTが吐き出すPHPコードには、「{」と「}」を使わずに、1行に1命令で終わっている行があります。これを意識せずに単純に改行文字を削除すると、行(命令)の終わりが正しく解釈されず、動かなくなってしまいます。
(この現象(の理由)に気付くまで、数日間悩みました)

 その他の行は、ファイルの操作に関するものです。

 CompressFile()は、$FileNameで指定したファイルについて、念のためバックアップを取ります。次回以降に呼び出された際、オリジナルのファイルとバックアップのファイルのタイムスタンプを比較して、オリジナルのファイルが前回より更新されていたら、ファイルを圧縮します。
(更新されていなければ、処理をスキップします)

 これは、次回以降に紹介する(かも知れない)、MTが吐き出すサイトマップ情報(sitemaps.xml)を元に、圧縮すべきファイルを抽出する(更新されたファイルのみ圧縮し、処理を効率化する)ためのものです。

 なお、この処理により、SOARISTO工房blogのPHPコードは、平均で16%程度圧縮(軽量化)することができました。

 また、生成されたPHPコードは、改行文字のまったくない数珠繋ぎのものとなるため、いわゆるパチリ屋(コピペ小僧)の解析意欲を喪失させる、という副次的な効果もあります。