SOARISTO工房 Logo
Computer Archive
2011/09/10

 またも、Webサーバ内部の改造です。

 工房では、工房にアップされているすべての画像への直リンクを禁止しています。

 具体的には、「.htaccess」に、以下のように記述しています。

 「Referer」の値をチェックして、Refererがセットされていない場合(=直リンクされている場合)、または工房のドメイン以外の場合(=自分のページに勝手に表示している場合)には、アクセスさせないようにしています。

#こうすると、セキュリティーソフトを入れていて、Refererを出力しないような設定をしている場合には、画像が表示されなくなってしまいますが、まぁ、そういうヒトは、そういうヒトということで。

 ただし、その一方で、Googleなどの画像検索クローラーがやってきた場合には、アクセスさせたい訳です。

 上記のように、ホスト名を指定して許可すればいいのですが、Googleのようなクローラーであっても、ホスト名が得られず、IPアドレスしか分からない場合があります。

 このような場合、そのIPアドレス毎に許可してやればいい訳ですが、クローラーのIPアドレスは、アクセスの度にコロコロ変わることがほとんどです。

 そこで、クローラーのIPアドレスを含むサブネット全体を、丸ごと許可してやることにします。

#ちょうど、スパム対策の逆の手法ですね。

 では、どのようにクローラーのIPアドレスを取得するかというと、.htaccessに、以下のように設定します。

 これは、HTTPのステータスコードに基づき、クライアントエラーが発生した場合には、“error.phpを呼び出す”という設定になります。

 1行目は、エラーコード403(Forbidden)で、リソースへのアクセスを拒否された場合に、2行目は、エラーコード404(Not Found)で、リソースが見つからなかった場合に、それぞれerror.phpを呼び出すことになります。

 これを受けて、error.phpでは、以下のようなHTMLを出力します。

 あたかも、Apacheとまったく同じ挙動をするように偽装していますが、実は、裏ではしっかりアクセスログを取っています。

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

 このスクリプトによって集められたIPアドレスを逆引きし、Googleなどのクローラーであった場合には、そのサブネットを得て、アクセス許可リストに追加していきます。

 逆に、直リンクしたり、自分のページに勝手に表示したりしている場合には、アクセス拒否リストに追加していきます。

 また、ある一定時間以内に、ある一定回数以上のアクセスがあった場合には、Webサーバへのアタックと見なして、アクセス拒否リストに追加したりできます。

 実際のエラーログの一部です。

 IPアドレスを逆引きすると、Googleが取得したサブネットからのアクセスであることが分かります。

 これらより、.haccessには、上記のように設定しました。

 このように、HTTPのステータスコードを取り込み、エラーログを解析することにより、「クローラーには優しく、不届きなヤカラには厳しい」対処を取ることができます。

2011/09/04

 藤本壱さんが開発された、「ブログに記事を書いたことをFacebookに投稿するプラグイン」を導入し、改造してみましたので、ご紹介します。

 オリジナルのプラグインの導入方法は、こちらまたはこちら小粋空間さん)をご覧ください。

fbpost01.jpg

 このプラグインは非常に有効で、MovableTypeにブログの記事(またはウェブページ)を投稿すると、自動的にFacebookのウォールにアップしてくれます。

 自動投稿されたイメージは、上記のようになります。

 とても便利なプラグインなのですが、Facebookに投稿される画像は、プラグインの設定時に指定した、固定的なものとなります。

 記事の内容によっては、その記事に関連する画像を設定して、ウォールにアップしたい場合があります。

 ということで、さっそく改造してみました。0xF9F8

fbpost02.jpg

 ファイルを所定のフォルダにアップし、最初に管理画面にアクセスすると、上記のようなメッセージが表示されるので、「アップグレード開始」を押下します。

fbpost03.jpg

 アップグレード(データベースの設定変更)が正常に終了すると、上記のようなメッセージが表示されます。

fbpost04.jpg

 ブログ記事の新規編集画面を開くと、右下に、上記のような項目が追加されています。サムネイル画像のURLを設定できるようになっています。

 サムネイル画像の初期値は、プラグインの設定時に指定したものが、そのまま入っています。

 過去に投稿した記事についても、編集画面を開き、「Facebookに公開」のチェックボックスを入れて再投稿すれば、その時点でウォールにアップしてくれます。

fbpost05.jpg

 改造後のイメージは、上記のようになります。

 記事に応じて画像を変えることで、一目でその記事の内容を推定することができ、訴求力をアップすることができます。

 以下、改造のポイントです。

 まずは、「config.yaml」の変更点です。

 サムネイル画像のURLを格納するフィールドを、「fb_thumbnail」とします。

 このフィールドを、ブログの記事(mt_entry)毎に紐付けるため、上記、4行目のように宣言します。

 ここで、fb_thumbnailには、「meta」という属性を設定しています。

 これは、過去のすべての記事(mt_entry)にfb_thumbnailを確保するのではなく、プラグインを導入した以降に投稿した記事(または、必要に応じて編集した過去の記事)に対してのみ、fb_thumbnailを確保するようにするためです。
(内部的には、「mt_entry_meta」にfb_thumbnailが追加されます)

 つづいて、「Plugin.pm」の変更点(一部)です。

 記事の編集画面で呼び出される関数(edit_entry)です。

 サムネイル画像のURLを設定するため、<input>タグを追加しています。

 ブログ記事を新規編集する場合には、変数「$fb_thumbnail」に、プラグインの設定時に指定した値を代入し、これをデフォルト値としています。

 過去に投稿した記事を編集する場合には、デフォルト値は同じですが、mt_entryのmetaフィールド「fb_thumbnail」に値が格納されていた場合(=過去に編集した際に設定していた場合)には、$fb_thumbnailにその値を代入しています。

 今回は<input>タグを使って、サムネイル画像のURLを直接設定するようにしましたが、同じ編集画面内の「ブログ記事アイテム」から、その記事に紐付けられた画像をドラッグ&ドロップできるようにすれば、もう少し便利になるかと思います。

 が、とりあえずは、基本機能の実装ということで・・・。0xF9C7

 これまで、MTと連携する外部関数は、いくつか書いてきましたが、MTのプラグインを作成するのは、初めての挑戦でした。著名な藤本壱さんが書かれたソースを解析してみて、とても勉強になりました。

2011/08/30

 効果テキメンでした。0xF9F8

spam01.jpg

 前回、スパム認定されたIPアドレスを含むサブネット全体を、丸ごと「完全遮断」するという強硬手段に出た訳ですが・・・、

 上記は、ここ1ヶ月半ほどのスパムコメント数の推移です。

 規制を掛ける前は、最大で1日8件、平均で1日4.3件のスパムコメントが付いていましたが、スパム認定されたIPを規制することで、1日平均2.6件にまで落とすことができました。

 さらに、スパム認定されたIPアドレスを含むサブネット全体を規制することで、1日平均0.2件(ここ数日は0件)にまで落とすことができました。

 してやったり、という感じです。

 これだけ効果が大きいものとは、当初は思いも寄りませんでした。しかも、そのほとんどが中国(cn)からのアクセスなので、実効上、なんら問題はありません。

 ついでに、PHPも、さらなる改善を図りました。

 前回のスクリプトを書いている途中に気付いたのですが、IPアドレスを解決する際に、わざわざAPNICのWebサーバに聞きに行かなくても、自分のサーバでwhoisコマンドを実行してその結果を得れば、HTTPプロトコルによるオーバーヘッドを無くせるので、レスポンスの改善を図ることができます。

 ということで、さっそく改良しました。以下、そのソース(一部)です。

 「ip2cidr()」は、IPv4形式のIPアドレス(開始アドレスと終了アドレス)から、ネットアドレスとサブネットマスクを生成する関数で、PHPの電子マニュアルに載っていたもの、そのままです。

 「GetNetMask()」が、今回のポイントとなる関数で、IPアドレスからネットアドレス等の関連情報を取得するものです。

 「exec()」関数でwhoisコマンドを実行し、結果をバッファに入れます。バッファを検索し、「inetnum」という文字列があれば、IPv4形式のIPアドレス(開始アドレスと終了アドレス)を取得します。

 あわせて、「country」という文字列があれば、国籍を取得します。

 なお、IPアドレスによっては、「inetnum」がwhoisサーバのデータベースに載っていない場合があるため、その場合には、例外処理をしています。

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アドレスがつぎつぎに増えているため、完全に抑え込むことはできませんが、システムが日々自動的に追加していってくれるので、管理的には楽になりました。