方式
apacheで同一IPアドレスからの連打を検出した場合アクセスを拒否するか、
特定の静的なページに飛ばすことでサーバの負荷を低減させる。
有名なのは下記の2つだが、今回は mod_dosdetector を選択した。
mod_evasive
即アクセス禁止になるため、特定のエラーページ等への遷移ができない(かもしれない)。
目的からすれば充分だが、エンタープライズでは
意図せぬ連打(F5の上に本置きっぱなし状態とかも)した顧客に
いきなり403を見せるというのは結構キツイ。
何より、apacheをpreforkで動かしている場合
子プロセス単位の個別でアクセス数がカウントされるため
(サーバ単位でアクセスをカウントしてくれないため)
httpサーバやpreforkの設定次第では、結構連打したけど防いでくれないとか、普通にあり得る。
mod_dosdetector
こちらは同一IPから一定時間内に一定数以上のアクセスを検知したら
環境変数を立ててくれるだけ。二段階方式で環境変数をみてのアクセス制御をいれなくてはならない。
多少面倒だが、融通は効く形式になるしpreforkでもきちんと動く。
mod_dosdetector をインストール
$ wget http://ncu.dl.sourceforge.net/sourceforge/moddosdetector/mod_dosdetector-0.2.tar.gz
$ tar -xzvf mod_dosdetector-0.2.tar.gz
$ cd mod_dosdetector-0.2
$ make
$ make install
Libraries have been installed in:
/usr/lib64/httpd/modules
apacheの設定
1分間に100回アクセスがあれば、Dos攻撃とみなす設定の例。
LoadModule dosdetector_module /usr/lib64/httpd/modules/mod_dosdetector.so
# 検出を行うかを指定。ここをoffにしてapache再起動で無効化可能
DoSDetection on
# 連打の検出をカウントする秒数を指定。以下は1分間。
DoSPeriod 60
# Dosの疑いありの閾値を設定、現在は利用していない。
# 「DosHardThreshold」に合わせておく。以下は100回アクセスで疑いありとみなす。
# 閾値に達すると環境変数SuspectDoS=1がセットされる
DoSThreshold 100
# Dosとして明確にみなすの閾値を設定。判定はこれを利用している。
# 800回アクセスでDos攻撃とみなす。
# 閾値に達すると環境変数SuspectHardDoS=1がセットされる
DoSHardThreshold 100
# 閾値に到達してからセットした環境変数がクリアされるまでの秒数を指定。
# 以下は5秒で判定を解除し、再判定に入ることを示す。
DoSBanPeriod 5
# 保持するIPの履歴を指定。
# 実際のIP種類は下記設定より遥かに多いが
# Dos判定に利用する=連続でリクエストが来るため、さしあたって3000で設定。
DoSTableSize 3000
# 判定から除外するMIMEタイプを指定
DoSIgnoreContentType image|javascript|css
これで設定は終わるが・・・
あくまで環境変数立てるだけなんで、連打を検出した振る舞いを続けて入れてみる。
## 連打対応
# ドメインを指定
RewriteCond %{HTTP_HOST} www\.foo\.co\.jp
# Dos攻撃判定(上記で設定される)を確認
RewriteCond %{ENV:SuspectHardDoS} .+
# 対象外のURL指定したければここで
RewriteCond %{REQUEST_URI} !^/hoge/.* [NC]
# 同じようにDos検出時に遷移させるページも当然除外
RewriteCond %{REQUEST_URI} !^/error/dos.page [NC]
# Dos判定されたらエラーページに飛ばしてあげる
# /inner で始まるUELは社内とかアクセス用なので除外とかも設定できる
RewriteRule !^/inner/(.*) http://xx.xx.xx.xxx/error/dos.page [P,L]
apacheの再起動で終わり
$ service httpd restart
補足
DOS攻撃を防ぎたいくらいの規模の場合は
数台のWeb(httpd)サーバをLBで繋いでいる環境が殆どだ思う。
上記の設定はあくまで1台単位なので、
LBを介した クライアント と Web(httpd)サーバ 間の接続が
stickinessなのか、Round Robinなのか確認して閾値を設定する必要がある。
もしRound Robinの場合は多少雑だが
(防ぎたい回数の閾値 / サーバ台数) の値を各apacheに設定するのが良いと思う。
あと、エンタープライズで、企業ユーザの場合は
大抵企業から外に出るグローバルIPアドレスは1つ(ないし数個)なので
これは個人単位ではなく、企業単位のアクセス制限になる、ということは意識しておくと良い。