概要
運用しているWebサイトのアクセスログをみると,海外のサーバからひっきりなしに攻撃を受けていることに気づいたので,簡単な防御策を講じました.
具体的な方法は以下の通りです.
- アクセスログを読み込み,海外IPからのアクセスがあれば
iptables
でブロックするようなスクリプトを作成. - そのとき,Googleなどの検索エンジンのクローラーはブロックしないように注意する.
- アクセスされた海外IPが新しいものか判断するために,過去ブロックしたIPはdenyIpファイルに保存しておく.
-
cron
で5分おきにスクリプトを走らせ,新たな海外IPからのアクセスがあればブロックリストに追加する.
アクセスされた時に即座に対応できる即時性はありませんが,素早く対策を講じることを優先しました.
開発環境
- CentOS 6.7
実装方法
スクリプトはPHPで書いています.
準備
IPアドレスから国を取得するためのPHPモジュール(GeoIP)
GeoIPを使うのでサーバに導入.
# peclコマンドを使うので必要であればpearパッケージもインストール
$ yum install php-devel php-pear geoip geoip-devel
$ pecl install geoip
# モジュールを使えるようにするために/etc/php.iniに追加
extension=geoip.so
アクセスログの形式
ddd.ddd.ddd.ddd - - [24/Apr/2016:04:24:04 +0900] "POST /hoge HTTP/1.1" 200 fugafuga
先頭にIPアドレスが記述してある形式とします.
実際のスクリプト
cron
上で以下のように,引数にアクセスログのファイルを与えて,5分おきにスクリプトを走らせることを想定しています.
cron
*/5 * * * * php /path/to/denyForeignIp.php /path/to/access.log
denyForeignIp.php
<?php
// 引数があるか確認
if (!isset($argv)) {
echo "You need a argument of a access log.";
exit();
}
// 除外するIPとホスト名(自分のIPや検索エンジンのクローラーは除外)
$excludeIp = array("ddd.ddd.ddd.ddd");
$excludeName = array("google", "msnbot");
// ログファイルからIPを全取得
$file = fopen($argv[1], "r");
$spamIp = array();
while ($line = fgets($file)) {
$result = preg_match("/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/", $line, $m);
if (!$result) continue;
// 除外対象のIPは取得しない
$hostName = gethostbyaddr($m[0]);
if (!in_array($m[0], $excludeIp))
foreach ($excludeName as $value) {
if (strpos($hostName, $value) === false)
array_push($spamIp, $m[0]);
}
}
$spamIp = array_unique($spamIp);
fclose($file);
// 外国IPのみを有害IPとする
foreach ($spamIp as $key => $value) {
$countryCode = @geoip_country_code3_by_name($value);
if ($countryCode === false) continue;
if ($countryCode === "JPN") {
unset($spamIp[$key]);
}
}
$spamIp = array_values($spamIp);
// アクセスされた海外IPが新しいIPだったらiptablesに追加
foreach ($spamIp as $key => $value) {
$existFlg = false;
$denyIpFile = fopen("/path/to/denyIp", "w+");
while ($denyIpLine = fgets($denyIpFile)) {
$denyIpLine = str_replace(array("\r\n", "\r", "\n"), "", $denyIpLine);
if ($value === $denyIpLine) {
$existFlg = true;
break;
}
}
if (!$existFlg) {
fwrite($denyIpFile, $value.PHP_EOL);
exec("iptables -A INPUT -s ".$value." -j DROP");
}
fclose($denyIpFile);
}
// ipteblesを保存
exec("/sbin/service iptables save");