迷惑なWebアクセスをブロックしよう

以下のテキストは、執筆時当時の情報を元に書いたものであり、 現在の情勢にそぐわないことを含む場合があるので注意されたい。 また、テキストは最終提出原稿で校正を経る前のものなので、実際にUNIXUSER 本誌に記載されたものとは異なる。誤字脱字等そのままである。

致命的な誤り以外は加筆修正等は行なわないので情報の鮮度に気をつけつつ 利用して欲しい。

目次

==========================================
Part3 迷惑アクセスからのWebサーバの保護
==========================================

■
■増える迷惑アクセス
■

せっかくWebサーバを立てても、そこに来るアクセスは必ずしも望んだものばか
りではない。自己繁殖のための攻撃アクセスをし続けるワーム、暴力的に連続ア
クセスを繰り返して全コンテンツをごっそり何度も持って行こうとする無礼なロ
ボット。回線帯域のほとんどが「招かれざる客」のアクセスで埋めつくされてい
る、といったことは最近では珍しくなくなってしまった。

限りある回線を節約するため、危険なワームの繁殖活動からサーバを守るために
できることを考えて行こう。ここでは、Apacheを題材に既存のツールの組み合わ
せでできる効果的な方法に絞って紹介しよう。

■
■迷惑アクセスの種類と対策
■

サーバ管理者、あるいはWebコンテンツ配信者にとっての迷惑アクセス、つまり
できればアクセスして欲しくないクライアントには主に以下のものがある。

	(1) 著名なものを含めた検索エンジンの「上品な」収集ロボット
	(2) 暴力的な連続アクセスをする「無礼な」収集ロボット
	(3) ウィルスやワーム、クラッキングツールから発せられる攻撃アクセス

これらのうち(1)を防ぐ必要があるのは、検索エンジンからダイレクトに参照さ
れることをよしとしない特殊な目的を持つページ(それ自体が検索ツールになっ
ている場合など)だろう。(2)は回線とサーバの負荷軽減に留意している場合には
制限したいだろう。(3)はどんな条件でも遮断したい類だろう。

(1)〜(3)は、「相手(サーバ側)の意向をどれだけ尊重するか」という度合に違い
があるので、アクセスを回避するにはそれぞれ違う方式をとる必要がある。各段
階に応じたアクセス遮断方法を解説していこう。


■
■ロボット対策
■


●事前準備

先述の(1)と(2)、ロボットからの収集活動を抑止する方法を考える。そのために
は、ロボットが手元のWebサーバにアクセスするときに名乗って来るユーザエー
ジェント名を知る必要がある。Apacheの設定ファイル(httpd.conf)のログ出力の
部分を書き換えておこう。デフォルトの httpd.conf ではログに関する部分は以
下のようになっている(抜粋)。


	(アクセスログの定義)
	CustomLog logs/access_log common

	(上記の "common" ログフォーマットの定義)
	LogFormat "%h %l %u %t \"%r\" %>s %b" common

デフォルトの状態にも、ユーザエージェントを含めたログフォーマットが定義さ
れている(実際には1行)。

	LogFormat "%h %l %u %t \"%r\" %>s %b
	 \"%{Referer}i\" \"%{User-Agent}i\"" combined

これをアクセスログに指定しておこう。

	CustomLog logs/access_log common
		↓
	CustomLog logs/access_log combined

httpd.conf 修正後、apacheを再起動するとログファイルの各行が以下のように
なる(実際には1行)。

218.219.192.204 - - [08/Sep/2004:23:32:27 +0900] "GET /%7Eyuuji/ HTTP/1.1" 200 2529 "-" "Mozilla/5.0 (X11; U; NetBSD i386; en-US; rv:1.6) Gecko/20040306 Firefox/0.8"

このうち、""で括られた最後の項目がユーザエージェント名で、ロボットの種類
に応じて異なるエージェント名が記録されるだろう。ただし、悪意に満ちたロボッ
トは嘘のエージェント名を名乗ることがある。それゆえ、ロボットの種類に応じ
たアクセス制御はあくまでも悪意を持たないエージェントにたいして、こちらの
希望を「お願いしてみる」程度の意味合いであることを肝に銘じておこう。そう
書くと効果が弱いように思えるかもしれないが、自サーバにアクセスする人はメ
ジャーな検索エンジンから来ることがほとんどなので、メジャーな検索サイトの
利用しているロボットに「お願い」できれば、この目的は達成できるといえる。

●「上品な」ロボット対策

慣習的に収集ロボットは、Webサーバ内に配置された /robots.txt というファイ
ルを取得し、そこに書かれた内容にしたがって収集するコンテンツのパス名を限
定するように設計されている。robots.txt ファイルに関する情報は、

http://www.robotstxt.org/wc/robots.html
http://www.robotstxt.org/wc/exclusion.html
http://www.robotstxt.org/wc/norobots-rfc.html

にまとめられている。この文法はとても単純である。

	フィールド:	値
	フィールド:	値
	<空行>

つまり、「フィールド」+「:」のあとに「値」を書いたものを1行毎に列挙する。
単純な具体例を挙げると以下のようになる。

	User-Agent:	*
	Allow:		/unixuser/
	Disallow	/
	<空行>

これは、
	* 全てのユーザエージェントに対し
	* /unixuser/ で始まるパス全て、つまり /unixuser/ 以下全てのファ
	  イルの取得を許可し、
	* それ以外の / 以下のコンテンツ全ての取得を禁止する

を意味する。

User-Agent, Allow, Disallow に指定する値はすべてにおいて大文字小文字を無
視して照合される。

User-Agent フィールドの値には、ロボットの名乗るユーザエージェント名(の部
分文字列)を指定する。それにマッチするユーザエージェントは、空行で区切ら
れた部分までを、自身に対する司令と見なす(ことが期待される)。

Allow と Disallow にはディレクトリ単位でパターンを指定できる。
robotstxt.org の標準によれば、

	* 複数のパターンを列挙した場合には最初にマッチしたものが採択され
          る(ファーストマッチルール)
	* ひとつもマッチしなかった場合のデフォルトアクションは Allow

となっているが、現実のロボットの挙動はこれと異なる場合がある。たとえば、
Google の走らせているロボット(Googlebot)では、ファーストマッチルールでは
なく最長マッチルールが適用される【註 い】。つまり、
---[註 い]------------------------------------------------------------
http://www.google.co.jp/webmasters/faq.html#robots 参照。
----------------------------------------------------------------------

	User-Agent:	googlebot
	Disallow	/
	Allow:		/unixuser/
	<空行>

のように書かれていたときに

	/unixuser/index.html

を取得しようとする場合、先に書かれているパターン / にマッチすることにな
るが、次のルール /unixuser/ に「より長いパス名」でマッチするため、最終的
に Allow と解釈される。

このように、実際のロボットによって若干の解釈の違いがあるが、
robotstxt.org の標準にしたがってファーストマッチルールで書いておけば大概
のロボットに有効な司令となるだろう。それ以上きめ細かい司令を特定のロボッ
トに施したい場合は、それを動かしている検索エンジンのWebページからロボッ
トの制御の仕方を探して参照すると良いだろう。

ただひとつ覚えておく必要があるのは、「robots.txtの効果が波及するのはその
内容が持って行かれてから」という点である。「うまくいかない」などと頻繁に
修正してはかえって期待に反することになるので、気長に待つように心掛けたい。

●「下品な」ロボット対策

管理者、あるいはコンテンツ発信者にとって全くなじみのないホストから
「御遠慮願いたい」収集活動が続く場合がある。

まず、Apacheのアクセスログを観察しよう。Apacheのログの書かれるディレクト
リに移動して

	tail -f access_log

としてしばらく眺める癖をつけていると、Webコンテンツをものすごい速さで自
動的に取得し続けるクライアントがあることに気が付くことがある。場合によっ
てはそのような暴力的なアクセスで、サイトの対外接続の回線幅を使い切ってし
まい、他のサービスに影響を及ぼすこともある。

前項で例示した Google などに代表される検索サイトでは、特定のWebサーバに
集中的な負荷が掛からないように配慮して収集ロボットを動かしている。ところ
が、個人の勝手でWebコンテンツ収集を行なっているようなホストからは暴力的
なアクセスが続けられることがある。これらの「下品な」ロボットからのアクセ
スをしりぞけたい場合は Apache のURL書き換えモジュール(mod_rewrite)が利用
できる。

・mod_rewrite モジュールの利用

mod_rewrite は汎用的なりクエスト書き換えモジュールで、要求されたURLを任
意のパス名やエラー等に置換することができる。置換するかしないかの条件を環
境変数の値によって決定させることができるので、User-Agentの種類に応じてエ
ラーを返したりすることもでき、それがここでの目的に合致する。

・mod_rewrite の構文

mod_rewrite の全貌については以下のURLに解説がある。
http://httpd.apache.org/docs/mod/mod_rewrite.html
今回行ないたいアクセス制限のためには、以下の3つのディレクティブが必要と
なる。

	*---------------------
	RewriteEngine  on|off
	*---------------------
	mod_rewrite による書き換えを有効化、または無効化する。
	デフォルトは off で、その場合全ての書き換えが無効化される。
	ただし、on/off の状態は文脈ごとに独立しているので、
	 ごとや  ごとに違う設定となる。書き
	換えが必要な文脈には全て RewriteEngine on を入れる【註 ろ】。

---[註 ろ]------------------------------------------------------------
筆者はそれに気付かずちっとも有効にならず苦労した。
----------------------------------------------------------------------

	*-----------------------------------------
	RewriteRule パターン 置換文字列
	または
	RewriteRule パターン 置換文字列 [フラグ]
	*-----------------------------------------
	要求URLを書き換えるルールを定義する。<パターン>で指定した正規
	表現にマッチする場合のみ、<置換文字列>に置き換える。<置換文字
	列>には Perl/Ruby 等で使えるのに似た、以下の特殊文字列が指定で
	きる。

	  * <パターン>中 ( ) で括ったN番目のグループを後方参照する $N
	  * 最後にマッチしたRewriteCond(後述)のパターン内のグループを
	    後方参照する %N
	  * サーバ変数 %{変数}
	  * マッピング関数【註 は】
---[註 は]------------------------------------------------------------
マッピング関数については mod_rewrite の Web ページを参照してほしい。
----------------------------------------------------------------------

	置換する必要がない場合には<置換文字列>のところに - (ハイフン)
	を指定する。

	省略可能な第3引数 [フラグ] には特別なアクションを指定できる。

%%%%	アクションは【表 に】に示した。
%%%%	(((表なんか載せる余裕はないですよね。省略してください)))
	今回利用するのはフラグ [F] で「Access Forbidden」を返す。

---[表 に]------------------------------------------------------------
RewriteRule の第3引数に指定できるフラグ

	--------+-------------------------------------------------
	 フラグ	|	意味
	--------+-------------------------------------------------
	 R	| Redirect 
	 F	| Forbidden (HTTP応答コード403)
	 G	| Gone (HTTP応答コード410)
	 L	| Last (Rewriteの動作をこれ以上行なわない)
	 N	| Next (最初のRewriteルールからもう一度繰り返す)
	 C	| 次のルールにチェインされる
	 T=type	| MIMEタイプを type にする
	 NS	| サブリクエストであるときにルールをスキップする
	 NC	| No Case (パターンで大文字小文字を区別しない)
	 QSA	| Query String Append (文字列置換ではなく追加する)
	 NE	| No Escape (通常行なわれるURLエスケープをしない)
	 PT	| Pass Through (次の書き換えエンジンに渡す) 
	 S=NUM	| 次のNUM個のルールをスキップする
     E=VAR:VAL	| 環境変数 VAR に VAL を代入する
	--------+-------------------------------------------------
----------------------------------------------------------------------

	所定の条件を満たした場合に Forbidden を返すようにするルールは、

	RewriteRule . - [F]

	のように書けば良い。「所定の条件」を定義する場合には次の
	RewriteCond ディレクティブを利用する。

	*--------------------------------------
	RewriteCond 参照文字列 パターン
	または
	RewriteCond 参照文字列 パターン [フラグ]
	*--------------------------------------
	後続する RewriteRule による書き換え条件を指定する。<参照文字列>
	が <パターン> にマッチするときに書き換えることになる。
	RewriteCond は連続して指定することができ、その場合は全ての条件が
	満たされるとき、となる(AND結合)。条件をOR結合したいときは<フラ
	グ>の部分にORを指定する。また、パターンマッチのときに大文字小文
	字を同一視したい場合は NC フラグを指定する。複数のフラグを指定す
	るときはカンマ(,)で区切って列挙する。

・User-Agentによる切り替え

ここでは、アクセスしてきたUser-Agentの種類が特定のものにマッチした場合に
アクセスを禁止する例を示そう。クライアントの名乗るUser-Agentは、変数
HTTP_USER_AGENT に入る。これを利用して、User-Agent が FooSpider または
BarCrawler だった場合にForbidden エラーを返すようなルールを書くと以下の
ようになる。

	RewriteCond %{HTTP_USER_AGENT} foospider [NC,OR]
	RewriteCond %{HTTP_USER_AGENT} barcrawler [NC]
	RewriteRule . - [F]

●HTTP内対策の有効範囲

robots.txt, mod_rewrite いずれの方法も、クライアントがよほどの悪意を持っ
ていない限り効果を持つものである。もしこちらの望みを無視してサーバを陥れ
ようとするものであればこれらの手段を使えず次項で説明するものを使わなけれ
ばならない。もっとも、自サイトだけが集中的に狙われるということは滅多に起
きないことなので、あまり神経質になりすぎるのも考え物である。

---[コラム SEARCH攻撃ログの除去]--------------------------------------
読者のサーバの Apace の access_log をless等で閲覧し、

	"SEARCH

というパターンで検索してみて欲しい。もしかしたら、以下の文字列で始まる驚
異的に長いログ行が見つかるはずだ。

	"SEARCH /\x90\x02\xb1\x02\xb1\x02\xb1.........."

これはIISを攻撃対象としたものと言われている。1行が長すぎるのでそれだけで
ログファイルが肥大化する。リクエストそのものの影響はないとはいえ、巨大な
ログ出力が書かれるせいでログの解析が困難になるし場合によってはファイルシ
ステムを圧迫する危険もある。このログが吐かれないようにしてしまおう。
Apacheの場合長すぎるリクエストはクライアントに応答コード414を返す。
Custom_Log ディレクティブでは、応答コードによってエントリを出力するかど
うかを決めることができる。例えば、標準のログフォーマット common を、「応
答コード414ならリクエスト出力しない」ように変えるには以下のように変更す
れば良い。

	LogFormat "%h %l %u %t \"%r\" %>s %b" common 
	↓
	LogFormat "%h %l %u %t \"%!414r\" %>s %b" common 
----------------------------------------------------------------------


■
■攻撃的アクセスからの保護
■

ウィルス/ワーム/クラッキングツール等から発せられる攻撃的アクセスは、OSも
しくはCGIプログラムの有名な脆弱性を狙ってくる。そうした攻撃は、何も自サ
イトそのものへの侵入を企ててくるわけではなく、とにかく「侵入できればどこ
でも良い」と手当たり次第にアクセスされているに過ぎない【註 ぬ】。

---[註 ぬ]------------------------------------------------------------
もちろん自サイトが侵入対象として狙われることが皆無とはいえないが、
よほど著名なサイトでない限り確率的にはゼロに等しい。
----------------------------------------------------------------------

そのような場合、プログラムによる自動攻撃が繰り返されることになるため、一
般的なWebサーバにありがちなセキュリティホールを「下手な鉄砲数撃ちゃ当た
る」方式で狙って来る。とくに多いのはWindows系サーバを狙ったアクセスで、
たとえば以下のようなログを残すアクセスが行なわれたりする(実際には1行)。

	61.97.128.70 - - [04/Sep/2004:18:11:24 +0900]
	"GET /c/winnt/system32/cmd.exe?/c+dir HTTP/1.0" 200 164 "-" 

これは筆者の運用するサーバにあったアクセスだが、実際には同じホストからパ
ス名やオプションを変えた似たような「試し撃ち」アクセスが何度も連続して行
なわれている。もっと言えば、HTTPに限らずポートスキャンを絡めつつ様々なプ
ロトコルでの攻撃的アクセスがそのホストから繰り返されることも警戒せねばな
らない。

上記のログでは、悪意あるクライアントが「サーバがWindowsだ」と仮定して、
C:ドライブにあるコマンドインタプリタ(cmd.exe)が起動できるかを確かめよう
としている。サーバとしてUnix+Apacheを仮定している場合でもApacheのデフォ
ルトに配備されているCGIの起動を試みたりする自動攻撃アクセスなどがある。
そうしたアクセスがあったときには、その時点でそのホストからのアクセスを一
切禁止してしまって構わないし、その方が安全である。

このような場合には、「試し撃ち」が試みられる典型的なパス名へのアクセスを、
「自動遮断スクリプト」の起動に向けてしまう方法が有効である。この方法は、

	特定のパス名へのアクセス
	↓
	自動遮断スクリプト起動
	↓
	クライアントのIPアドレスからのアクセスをパケットフィルタで遮断

という流れによるもので、既存のツールで実現できる単純なものである。

●Apacheの設定確認

まず、ApacheでCGIスクリプトを認識させる必要がある。httpd.conf で

	AddHandler cgi-script .cgi

を有効にしておこう。

もう一点、のちほどパケットフィルタリングツールをhttpdから起動させるため
の事前情報として、httpdの実行権限を知っておく必要がある。具体的には、
httpd.conf 内の User ディレクティブの記述を確認する。もし、

	User www

という行があれば、httpdは「ユーザ=apache」で起動されていることになる。読
者のサイトの設定を確認して欲しい。ここでは、httpd の実行権限が www であ
ると仮定して解説を進める。


●自動遮断スクリプトの作成

典型的な「攻撃のためのパス名」へのアクセスは、Apacheの持つ Alias 機能を
用いてCGIスクリプトにむける。ただしCGIスクリプトとはいってもクライアント
からの入力を受けたりするわけではなく、クライアントのIPアドレス(環境変数
REMOTE_ADDR に入っている)をパケットフィルタリングツールに渡して終了、と
いう一方的な(クライアントとのデータの授受を伴わない)ものである。

作成するスクリプトは以下の動作を行なうように作成する。

	1. 標準出力に「アクセスを遮断しました」のようなメッセージを出力
	   (httpdによってクライアントに渡されることになるので、
	    Content-type: plain/text などを指定しておく)
	2. 環境変数 REMOTE_ADDR をアクセス遮断データベースに追加する
	3. 実際のパケットフィルタリングを行なうコマンドを起動

以上の要件を実現するにあたり、具体的な部分は以下のような設計とする。

	* 作業ディレクトリは /var/wwwblock とする(以下 $DIR と表記する) 
	* 遮断したいIPアドレスは $DIR/ipdir/ ディレクトリの中に同名のファ
	  イルを touch することでデータベースに登録されたものと見なす
	  (コラム参照)
	* 上記(3)を実行するコマンドは sudo と make を組み合わせて実現す
	  る(パケットフィルタ操作にはroot権限が必要なため)

---[コラム ファイル名でデータを表現するdatadir形式]-------------------
特定のIPアドレスからのアクセスを一定時間拒否したいような場合、そのIPアド
レスをデータととらえるとデータが登録された時刻も保存する必要がある。そう
した場合にはなんらかのデータベースシステムを使うのが常套手段であるが、も
しデータの名前が簡単なASCII文字で構成されるとの仮定が置けるなら、datadir 
形式で管理すると良い。datadir形式とは筆者の造語で、特定のディレクトリに
データ名と同じ名前のファイルを作成することでそのデータの登録を意味させる。
データの削除は同名のファイル削除することで行なう。

たとえば、ディレクトリAに以下のようなファイルがあったとする。

-rw-r--r--  1 yuuji yuuji 0 Sep 10 06:57 10.3.4.5
-rw-r--r--  1 yuuji yuuji 0 Sep 10 07:34 10.9.8.7

これは、2つのデータ 10.3.4.5, 10.9.8.7 が登録されている状態を意味し、各
データの登録時刻はファイルのタイムスタンプが表している。登録されたデータ
一覧を取り出すにはlsコマンド等を利用すれば良い。一定時間が経過したデータ
を消したい場合はfindコマンドの -mtime, -mmin オプションなどを利用して該
当するファイルを消せば良い。各データの登録と削除は互いに影響を及ぼさない
のでロックせずに複数のプロセスで同時に更新してもデータベースが破壊されない。

時刻管理とロック処理をOSに任せられるので、datadir形式を利用したスクリプ
トは非常に簡潔に書ける。IPアドレス、ホスト名、メイルアドレス、といった構
成文字がASCII文字に限られるものを管理する場合は、datadir形式を利用すると
効率的に管理スクリプトが書けるだろう。
----------------------------------------------------------------------

以上をふまえて、自動遮断(CGI)スクリプト(block.cgiとする)は、【リスト る】
のように作成する。作成に先立って、作業ディレクトリとデータベースを格納す
るディレクトリを適切に作成する。

	# mkdir -p /var/wwwblock/ipdir
	# cd /var/wwwblock
	# chown www ipdir
	# vi block.cgi
	  (リスト る のCGIスクリプトを作成し保存する)


---[リスト る block.cgi]----------------------------------------------
#!/bin/sh
PATH=${PATH}:/usr/local/bin
# CGIとして動くので Content-type を出力
echo 'Content-type: text/plain'
echo

# クライアントに送るメッセージ。「遮断しました」という旨が
# 伝わればどんなものでも良い。ただ実際は攻撃プログラムに渡るだけ
# なのでこのメッセージを人間が目にするのは外部から自動遮断が
# 効いているかを確認するときだけだろう。
echo "Your PC ($REMOTE_ADDR) should be infected by some virus."
echo 'We block your connection for one day.'
echo 'Please do virus-scan your PC and visit us on the day after tomorrow.'

cd /var/wwwblock
# sudo に PATH が通っていることを確認
touch block
# callmake.sh はディレクトリ移動後makeを呼ぶだけのスクリプト(後述)
sudo /var/wwwblock/callmake.sh >> blocklog 2>&1
----------------------------------------------------------------------

現実に、パケットフィルタリングをするコマンドはmakeを利用して作成する。
その前にシステム付属のパケットフィルタリングツールで、アクセスを拒否する
ときの書式を確認しておこう。遮断したいIPアドレスを X.Y.Z.W と仮定した場
合、代表的なフィルタリングツールでは以下のような書式で遮断できる【註 わ】。

【ipfw/ipfw2】 (コマンド起動)
ipfw add <適当なルール番号> reset tcp from X.Y.Z.W to any

【IPfilter】 (ルールファイルの行)
block return-rst in log quick on <IF名> proto tcp from X.Y.Z.W to any

【iptables】 (コマンド起動)
iptables -I INPUT <適当なルール番号> -s X.Y.Z.W -p tcp -i <IF名> \
	-j REJECT --reject-with tcp-reset

---[註 わ]------------------------------------------------------------
各行の <IF名> の部分は、WebサーバがWANに接続しているNICのインタフェー
ス名。
----------------------------------------------------------------------


これらの遮断ルール行を自動生成し、既存のパケットフィルタルール(もしあれ
ば)に追加するような Makefile を作成する。

・ipfw/ipfw2の場合
  ================

  ipfw(とipfw2)の場合は、既に稼動しているパケットフィルタルールセットの
  任意の箇所に任意のルールを追加/削除でき、なおかつ指定したルール番号で
  登録したルールはつねに同じ番号であり続ける。このため、たとえば「自動遮
  断スクリプトで使うフィルタルール番号は99番」という約束にしておけば、既
  存のルールに影響を及ぼさずに、攻撃からの自動防御ルールを管理することが
  できる。

  仮に、読者のサーバの既存のipfwルールがルール番号100番から始まっている
  とすると、攻撃的アクセスはそれよりも先に配置すると良い場合が多い。もち
  ろんサイトの事情によって異なるが、この仮定が置けるとすると

	(1) ルール番号99番を全て消去
	(2) $DIR/ipdir/ ディレクトリの古いファイルは消去する
	    (24時間以上経ったものは消す)
	(3) $DIR/ipdir/ ディレクトリにあるファイル(IPアドレスを示している)
	    一覧からルール番号99のアクセス遮断行を生成する
	(4) 上記(3)で生成した行を実行する

  という処理を行なえば良い。Makefile では、以下のようにする。

  ---[ /var/wwwblock/Makefile ]-----------------------------------------
  # Makefile for ipfw www-attack blocker 
  # This Makefile should be located in /var/wwwblock
  BLKDIR	= ipdir
  RULENUM	= 99
  # httpdの実行権限となるユーザ名を HTTPDUSER に定義する
  HTTPDUSER	= www

  all:	ipfw.deny call-ipfw

  ipfw.deny:	${BLKDIR}
	@find ${BLKDIR} \( -type f -a -mmin +1440 \) -exec rm '{}' ';'
	/bin/ls ${BLKDIR} \
	 | awk '{printf "ipfw a 99 reset tcp f %s t a\n", $$1}' \
	 > ipfw.deny
	chown ${HTTPDUSER} ipfw.deny

  call-ipfw:
	-@ipfw del ${RULENUM} > /dev/null 2>&1
	sh ./ipfw.deny
  ----------------------------------------------------------------------


・IPfilterの場合
  ===============

  ipfwの例では、一定時間以上経過した遮断ルールを消すようにしている。これ
  と同じことをIPfilterでも行ないたいが、IPfilterは稼動している特定のルー
  ルだけを自動的に判定して消すことができないので、つねに全てのフィルタリ
  ングルールを登録し直すように Makefile を記述する(このやり方はiptables
  でも同様)。

  システムの既存のIPfilterルールを記述したファイルがあれば、それを
  /var/wwwblock/ipf.src にコピーしておく。そして、攻撃的アクセスをトリガ
  として自動的に生成された遮断ルールを ipf.src にマージしてipf コマンド
  に渡す、という方法を取る。

  ---[ /var/wwwblock/Makefile ]-----------------------------------------
  # Makefile for IPfilter www-attack blocker 
  # This Makefile should be located in /var/wwwblock
  BLKDIR	= ipdir
  # httpdの実行権限となるユーザ名を HTTPDUSER に定義する
  HTTPDUSER	= www

  all:	ipf.conf call-ipf

  ipf.conf:	ipf.src ${BLKDIR}
	@find ${BLKDIR} \( -type f -a -mmin +1440 \) -exec rm '{}' ';'
	(/bin/ls ${BLKDIR} \
	 | awk '{printf "block return-rst in log quick \
		proto tcp from %s to any\n", $$1}' ;\
	 cat ipf.src) > ipf.conf
	chown ${HTTPDUSER} ipf.conf

  call-ipf:
	ipf -Fa -f ipf.conf
  ----------------------------------------------------------------------


・iptablesの場合
  ===============

  システムの既存のiptablesルールを記述したシェルスクリプト等があれば、そ
  れを /var/wwwblock/ にコピー、またはシンボリックリンク作成しておく。仮
  にそれを setiptable.sh というファイル名だとすると、以下のような
  Makefile で実現できるだろう【註 か】。

  ---[ /var/wwwblock/Makefile ]-----------------------------------------
  # Makefile for iptables www-attack blocker 
  # This Makefile should be located in /var/wwwblock
  BLKDIR	= ipdir
  # httpdの実行権限となるユーザ名を HTTPDUSER に定義する
  HTTPDUSER	= www

  all:	ipt.sh call-ipt

  ipt.sh:	${BLKDIR}
	@find ${BLKDIR} \( -type f -a -mmin +1440 \) -exec rm '{}' ';'
	/bin/ls ${BLKDIR} \
	 | awk '{printf "iptables -I INPUT 1 -s %s -p tcp \
		-j REJECT --reject-with tcp-reset\n", $$1}' \
	 > $@
	chown ${HTTPDUSER} $@

  call-ipt:
	./setiptable.sh
	sh ipt.sh
  ----------------------------------------------------------------------

---[註 か]------------------------------------------------------------
iptablesの管理に熟練している場合はINPUTチェインそのものにルールを追加す
ることは行なわず、論理的なチェインを新規に作成してそこにルールを追加した
りすることが多いだろう。その場合は、適宜 Makefile を修正しサイトに合わせ
たルール構文を生成するようにして欲しい。wwwblock専用の論理チェインを作成
して、そこに集約させるのもひとつの手だ。
----------------------------------------------------------------------


●sudoの設定

wwwユーザが /var/wwwblock ディレクトリで make コマンドを起動する場合に限
り、root権限が与えられるようにする。これには sudo を利用する。visudoコマ
ンドを起動し、sudoers ファイルに以下の内容を追加する。

	www	venus=(root) NOPASSWD: /var/wwwblock/callmake.sh

callmake.sh は以下の内容とする。makeを起動する前に必ず所定のディレクトリ
に移動する処理を忘れないように注意する。

  ---[/var/wwwblock/callmake.sh]--------------------------------------
  #!/bin/sh
  cd /var/wwwblock || exit 1
  make
  --------------------------------------------------------------------

また当然のことながら、callmake.sh はroot以外誰も書けないように注意する。

	# chown root callmake.sh
	# chmod og-xw callmake.sh
	# chmod +x callmake.sh

●httpd.confの設定

典型的な攻撃アクセスが来たら、block.cgi が起動されるように httpd.conf を
修正して作業は完了する。

たとえば、/c/winnt/* という全てのパスへのアクセスが来たら block.cgi を起
動するようにしたい場合はhttpd.confに以下の Alias 定義を追加する。

	
	 Options ExecCGI
	
	Alias /c/winnt "/var/wwwblock/block.cgi"

世間(?)の動向とApacheのアクセスログを確認して、新種のワームがつつきに来
る新しいパス名が観察されたらAlias定義のパス名をどんどん追加して行くとよ
いだろう。修正後、Apacheを再起動して変更を反映させる。

●ブロック設定の確認

Alias設定が実際に活きていて、ブロック用のCGIスクリプトが機能しているかを
外部ホストからアクセスして確認しよう。

	外部ホスト% telnet webサーバ 80
		    ~~~~~~~~~~~~~~~~~~~
	GET /c/winnt/system32/cmd.exe?/c+dir
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	Your PC (*.*.*.*) should be infected by some virus.
	We block your connection for one day.
	Please do virus-scan your PC and visit us on the day after tomorrow.

	(更に続けて)
	外部ホスト% telnet webサーバ 80
		    ~~~~~~~~~~~~~~~~~~~
	telnet: connect to address *.*.*.*: Connection refused
	telnet: Unable to connect to remote host

このように、2度目のアクセスが遮断されれば設定はうまく行っている。遮断さ
れない場合は、

	* /var/wwwblock/ipdir にhttpdがデータを書き込めているか
	* httpd実行権限で callmake.sh を起動してエラーが出ないか

を確認する。

●その他のサービスでもブロック

今回はhttpdから攻撃ブロックスクリプトを呼ぶ例を紹介したが、これはhttpdに
限らず他のネットワークサービスでも利用できる手法である。たとえば、読者の
サイトで IMAP サービスを利用して「いない」場合は、imapポートの先で攻撃ブ
ロックスクリプトを起動するようにするのも一案である。そのような場合、
tcpserverを利用してスクリプトを起動すると良い。

	tcpserver -R サーバのIPアドレス ポート番号 ブロックスクリプト 

のような書式で実行しておけば、「ブロックスクリプト」が起動されるときには
接続クライアントの情報が、
	IPアドレス			→ 環境変数 TCPREMOTEIP
	ホスト名(逆引きできた場合)	→ 環境変数 TCPREMOTEHOST

のように代入されているので、スクリプトからこれらを参照することがとてもシ
ンプルに記述できる。今回紹介したHTTPをトリガとするブロックの手法に慣れて
きたら、色々なサービスのブロックを試してみると良いだろう。


yuuji@example.org
Fingerprint16 = FF F9 FF CC E0 FE 5C F7 19 97 28 24 EC 5D 39 BA
HIROSE Yuuji - ASTROLOGY / BIKE / EPO / GUEST BOOK / YaTeX [Tweet]