SOARISTO工房 Logo
Software Archive
2011/08/25

 前回、MT5と連携した形で、スパムフィルタを作りました。

 日々自動的に追加していってくれるので、便利なことは便利なのですが、踏み台とされるIPアドレスがつぎつぎに増えていっているため、イタチごっこ状態となっています。

 スパムと判定されたIPアドレスの一覧を見ると、ほとんどが中国からのアクセス(DNS解決できないものも含めて)ですが、特に

   *.dynamic.163data.com.cn

からのものが多くなっています。

 中国のプロバイダーにぶら下がっているユーザで、セキュリティーの甘いユーザーが踏み台にされて、各国のサイトに悪さをしているようです。
(ネットで検索すると、あちこちで被害が発生しているようです)

spam01.jpg

 ということで、いよいよ強制措置を取ることにします。上記中国のプロバイダーからのアクセスは、丸ごと「完全遮断」することにします。

 あわせて、MT5でスパム認定される毎に、IPアドレスを一つ一つちまちまと追加していたのでは日が暮れてしまうので(夜中に自動処理ですけど)、スパム認定されたIPアドレスを含むサブネット全体を、丸ごと「完全遮断」することにします。

 これらには、上記中国のプロバイダーのIPアドレスに加えて、DNS解決できないIPアドレスも含みます。

 工房としては、かなり思い切った措置となります。

 当然のことながら、まっとうなユーザーも影響を受けることになりますが、どーせほとんどが中国からのアクセスなので、ばっさり切り捨てます。
(不幸にも、セキュリティーリテラシーの弱い人たちがいるセグメントに含まれていたということで、諦めていただきます)

 上記を実現するためには、「IPアドレスから、そのIPアドレスが含まれるネットアドレスと、サブネットマスクを得る」という機能(関数)が必要となります。

 以下、そのソース(一部)です。

 IPアドレスからネットアドレス等の関連情報を取得するには、ご存知のとおり、「whois」というコマンドを使います。ネット上では、APNIC(アジア・太平洋エリアを管轄する組織)などのサイトで確認できます。

 こんな確認をいちいちやっていたのでは日が暮れてしまうので、自動的に取得できるようにする訳ですが、試行錯誤の結果、

   http://wq.apnic.net/apnic-bin/whois.pl?searchtext=(IPアドレス)&object_type=inetnum

というコマンド(URL)を与えることにより、ネットアドレスを取得でることが分かりました。

 「GetNetMask()」というのが、その関数です。

 PHPでは、Webサーバが出力するデータを、通常のファイルと同じように扱うことができます。

 上記URLをオープンして、出力される行を検索し、「inetnum」という文字列が含まれる行にある、IPv4形式のIPアドレス(開始アドレスと終了アドレス)を取得します。

 その開始アドレスと終了アドレスから、ネットアドレスとサブネットマスクを生成します。

 この関数を、いろいろ試行錯誤して作っていたのですが、なんのことはない、PHPの電子マニュアルに、そのままどんぴしゃの関数の例が載っていました。「ip2cidr()」という関数です。

 ということで、割と簡単に機能を強化することができまました。上記IPアドレスの一覧のうち、一番右のカラムに、「ネットマスク」(ネットワークアドレスとサブネットマスク)の情報を載せてあります。

 この情報を、Webサーバの「.htaccess」ファイルに、「deny from (ネットマスク)」として追加することにより、スパム認定されたIPアドレスを含むサブネット全体を、丸ごと「完全遮断」することができます。

 さて、その効果のほどはいかに・・・。

2011/07/30

 blogを「Movable Type 5」(以下、MT5)に変更しましたが、ここのところ、やたらとスパムコメントが付くようになりました。
(記事の更新情報をpingするにも、一長一短があります)

 MT5にもスパムフィルタがあり、コメントの内容を監査して、ある一定以上のスコアとなると、自動的に隔離されるようになっています。

 とはいえ、コメントをいったん受け付けてからスパム判定するようになっていることと、スパム判定した後も、そのIPアドレスからのコメントを拒否する訳ではないので、公開されないコメントが、裏でどんどん増えていくことになります。

spam01.jpg

 勝手にサーバのリソースを使われるのは、気持ちの良いものではないので、MT5と連携した形で、スパムフィルタを作ってみました。

 上記は、スパムと判定されたIPアドレスの一覧です。

 ホスト名からすると、大部分が中国のプロバイダからのものとなっています。また、DNS解決できないIPアドレスも、逆アクセスしてみると、中国の監督官庁のサイトであったりします。
(当局も、よもや不正アクセスの踏み台にされているとは、まったく気付いていないことでしょう)

 スパムフィルタの仕組みは簡単で、MT5がスパムと判定したIPアドレスを、SQLサーバから聞き出して、Webサーバの「.htaccess」ファイルに、「deny from (IPアドレス)」として追加するだけです。

 あわせて、確認用(というか、全世界に曝すため0xF9D1)に、スパムテーブルファイルを更新します。その内容を表示したものが、上記となります。

 この操作を、1日に1回、自動的に実行します。

 いったんスパムテーブルファイルに登録されたIPアドレスは、Apacheレベルで門前払いとなるため、以降、二度とアクセスすることができなくなります。

 これで、サーバのリソースを勝手に使われることもなくなりました。めでたし、めでたし。
(いっそのこと、中国からのアクセスは、すべて拒否ってやろうかとも思ったのですが、いまのところ思い留まっています)

 これまで「アクセスログ分析」のページはCGIで作っていましたが、これを機に、PHPに書き改めてみました。

 以下、スパムフィルタのソース(一部)です。

 はじめに、スパムテーブルファイル(spamtable.dat)を読み出し、配列($SpamTable)に格納します。

 つぎに、SQLサーバから、スパム判定されているコメントについて、IPアドレスと登録日時を、日付順に読み出します。
(MT5では、スパム判定されたものは、“comment_junk_status”が-1となっています)

 つづいて、この情報を、配列の内容と比較し、登録されていない場合には、配列の最後に追加します。

 最後に、スパムテーブルファイルを追記モードでオープンし、新たに登録された情報のみ、書き出します。
(念のため、バックアップファイルを作成するとともに、追記(書き込み)であることから、排他処理をしています)

 ということで、スパムフィルタができました。

 その効果ですが、スパムコメントの登録数が大きく低下し、サーバの負荷状態も改善したようです。

 スパムコメントは、踏み台とされるIPアドレスがつぎつぎに増えているため、完全に抑え込むことはできませんが、システムが日々自動的に追加していってくれるので、管理的には楽になりました。

2011/04/24

 工房では、開設当初から、アクセスログの解析機能を独自に作り込み、運用してきました。収集している情報の一部は、こちらで公開しています。

 その中で、「キーワード解析」というものがあります。これは、訪問者が、Googleをはじめとする各種検索エンジンにて、どのようなキーワードを使って検索し、工房に辿り着いたかを示すものです。

 苦労して作り込んだ割には、これまで放ったらかしにしていましたが、はたと、
   「これは、もしかして非常に貴重な情報なのでは?」0xF9A0
と思い付き、SEO対策として活用することにしました。

 蓄積したキーワード情報の生データです。(都合上、上位100位までを表示)

 これらは、各種検索エンジンからのリファラーから抽出したものです。
(若干、文字化けしたものが含まれていますが)

 よく見ると、検索エンジンで検索されたキーワード情報は、単体(1ワード)の場合もありますが、複数のキーワードから成っているものも、多くあります。

 ここで、例えばつぎのようなキーワード情報があったとした場合、

 "HID 取り付け" 500hit
 "HID バラスト" 300hit
 "バラスト 改造" 200hit
 "バラスト ハイワッテージ" 100hit

 キーワード単体での累積カウント数は、

 "HID" 800hit
 "バラスト" 600hit
 "取り付け" 500hit
 "改造" 200hit
 "ハイワッテージ" 100hit

となり、リファラーからのキーワード情報の順位とは、必ずしも一致しないということになります。
(当たり前ですが)

 よって、キーワード情報(以下、複数のキーワードを含むことから、「クエリ」と呼ぶことにします)に含まれるキーワード単体に注目し、その累積カウント数を調べることにより、高ヒット率のキーワードを抽出することができます。
(簡単にいうと、クエリを単体のキーワードに分割して、キーワード毎にカウントするということです)

#ついでに、半角やら全角やらがゴチャ混ぜになっているので、この際、キレイに整形します。

 PHPスクリプトは、とても簡単です。(keyword.php)

 まず、クエリを収めたデータファイル(keyword.dat)を読み出し、クエリとクエリのカウント数を、配列($LineArray)に格納します。

 つぎに、クエリをキーワードに分割する前処理として、「全角英数字を半角に」「全角スペースを半角に」「半角カナを全角に」「アルファベットの小文字を大文字に」変換します。

 つづいて、半角スペースを区切りとして、クエリをキーワードに分割し、連想配列($KeywordArray)に格納していきます。
(新しいキーワードが出てきた場合には、新たに連想配列を作成し、カウント数をセットします。過去に現れたキーワードであった場合には、カウント数を加算します)

 最後に、連想配列を、累積カウント数でソートしてから、上位100位までを表示し、終了します。

#日本語処理(マルチバイト文字列の処理)に、ちょっとしたコツが必要となりますが、こちらを参考に。

 実行結果です。

 当初、約2.7万個のクエリがありましたが、最終的に、約5.5万個ものキーワードに分解、整理されました。

 冒頭のキーワード情報の生データとは、順位が異なっていることにお気付きいただけるかと思います。

 
 で、これを何に使うかだって?

 それは、ヒ・ミ・ツ、ということで。0xF9F8

#クルマ関係のサイトとしては、非常に貴重な情報かと。

2011/04/23

 ときどき、みょ~にプログラムが組みたくなることがある訳で、0xF9BE

 工房blogのサイトマップは、MT5にテンプレートを組み込んで、エントリの作成時等に自動的に生成するようにしていますが、工房本体については、作っていませんでした。

 そこで、ネット上の「サイトマップ作成サービス」を使って生成したのですが、さすがに無料なだけあって、更新日時等がかなりいい加減です。0xF9A5

 仕方がないので、自分で作ることにしました。

 ついでに、SEO対策として、「とある工夫」をすることにしました。

 工房の各ページは、<frame>を使って構成していますが、<frame>を使ったページ構成は、Googleのサイトクローリングとは相性が悪いようです。

 とはいえ、いまさらページ構成を大きく変えるのは骨が折れるので、解説ページなどを参考に、対策を打つことにしました。
(どのような対策を打ったかについては、別の機会に述べることにします)

 まずは、その前段となる情報を生成します。

 PHPスクリプトは、とても簡単です。(sitelink.php)

 まず、サイトマップファイル(sitemap.xml)を読み出し、<loc>タグの間に記述されているURLを抽出し、配列($SitelinkArray)に格納します。

 つぎに、URLからサーバ上のソースファイルのパス($SitesrcDataName)に変換し、そのファイルが更新された日時を得て、配列に格納します。

 つづいて、サーバ上のソースファイルを読み出し、<title>タグの間に記述されているタイトルを抽出し、配列に格納します。

 最後に、配列を、サイトマッププロトコルに基づいてサイトマップファイル(sitemap-new.xml)を出力し、終了します。

 実行結果(画面)です。

 このソースファイルをコピペして、SEO対策に使います。

 実行結果(出力ファイル)です。

 オプション情報は、<lastmod>タグだけを出力し、<changefreq>や<priority>は出力しないことにしました。

2011/03/27

 前回のエントリでは、サムネイルをランダムに表示するための下準備をしました。今回は、いよいよその本題です。

 まずは、PHPのスクリプト(photogallery.php)です。

 スクリプトの流れとしては、
  • サムネイルのデータベース(thumbnail.dat)を読み込み、配列($AssetImageArray)に格納する。
  • 配列から、所定の個数($ThumbnailImageNum)分、ランダムにサムネイルを選定する。
    (選定の際には、すでに選定されたものが重複して選定されないようにする)
  • 選定したサムネイルへのタグ<IMG>と、サムネイルを含むエントリへのリンクのタグ<A>を出力する。
となります。

#「$BlogBaseURL」と「$BlogBasePath」は、利用する環境に合わせて適当に変更してください。(「$ThumbnailImageNum」は、お好みで)

#「$DebugFlag」を1とすると、実行中にいくつかの状態メッセージを表示することができます。

 上記スクリプトを、MT5の「テンプレートモジュール」に登録し、blogを再構成します。

thumbnail01.jpg

 実行した結果です。

 登録されているアイテム(画像)のサムネイルが表示され、サムネイルをクリックすると、サムネイルを含むエントリにリンクします。

 サムネイルのサイズ($ThumbnailWidthと$ThumbnailHeight)は、当初はサムネイルのデータベース(thumbnail.dat)の中に埋め込んでいました。その方が、blogの運用途中でサムネイルのサイズを変更したとしても、表示側(photogallery.php)で動的に対応できるからです。

 しかし、この方法だと、サムネイルの数が膨大になってくると、サムネイルのデータベースのファイルサイズも膨大になり、読み込み時にblogサーバに負荷が掛かってしまいます。(すべての情報を、一旦配列に読み込むため)

 これを避けるため、サムネイルのサイズは、定数としました。

#よって、blogの運用途中でサムネイルのサイズを変更した場合には、生成側(thumbnail.php)でForceオプションを使って強制的にサムネイルを再作成するとともに、表示側で定数を変更して、blogを再構成してください。

 同様に、サムネイルへのリンク($ThumbnailImagePath)と、サムネイルを含むエントリへのリンク($EntryLinkPath)も、登録されているアイテム(画像)のリンク($AssetImagePath)から生成するようにしました。

 サムネイルのデータベースに埋め込む情報を必要最小限に絞ることで、データベースのファイルサイズをかなりコンパクトにすることができました。

 ということで、ひとまず完成です。0xF9C6