パーク24グループのタイムズカーを皮切りに、GMOインターネットがサイバー攻撃を受けた大規模なセキュリティ事件など、Webの不正アクセスがここ数週間で続けて表に出ている。
そのような週に、自分のさくらVPSに入っている SiteGuard Lite の直近ログをまとめて見てみた。並んでいたのは、設定ファイルや環境変数ファイルの名前を直接たたくリクエスト、ディレクトリを逆上ろうとするリクエスト、このサーバでは動いていない別プロダクト向けの攻撃、見慣れない HTTP メソッドだった。
確認した範囲では、判定は BLOCK だった。目的は、今はアプリの手前で止まっている通信が、もしすり抜けたときに広がらないよう、入口を減らすことに置いた。
環境は、さくらのVPS、RHEL系、Apache httpd、firewalld、PHP(php-fpm)、SiteGuard Lite。中では WordPress や EC-CUBE など、複数のサイトが同居している。以下のホスト名とIPは、すべて例である。
ログの読み方
1行から拾ったのは、次の4つだけだ。
- いつ来たか
- どの種のURLか(設定ファイル名、管理系のパス、異常に長いクエリ、など)
- 判定が BLOCK か、検知だけで通しているか
- 同じ送信元が、短時間に何件来ているか
BLOCK は、そのリクエストがアプリケーションまで届いていない、という意味だ。件数の多さは、盛んに探されているという兆候であって、侵入の証明ではない。
この記事では、止め方を次の3語で呼び分ける。
- BLOCK … SiteGuard がアプリの手前で拒否する
- 403 … Apache がリクエストを拒否する
- DROP … firewalld がパケットを捨て、相手からは届かないように見える

方針
本命は SiteGuard の公式シグネチャにする。IPの一覧は補助に置いた。送信元は毎日替わるので、一覧を人手で追い続けることを主対策にはしない。
実施したことは、ログに近いものから次の順だ。
- 公開しなくてよいファイルを閉じる
- 使っていないホスト名を、外から中身が見えない状態にする
- アプリを系列内のセキュリティリリースに乗せ、アプリの管理画面を作業用回線だけにする
- バージョン文字列を応答ヘッダから外す
- 同じ失敗を繰り返すIPを、しばらく自動で遠ざける
- OSのセキュリティ更新だけを、再起動なしで自動適用する
- 突出した送信元だけ、補助として DROP する
- SiteGuard の管理ポートを、作業用回線だけにする

1. 出してしまっていた情報を閉じる
IPアドレス直打ちで、サーバ標準のドキュメントルートにある info.php が 200 を返していた。中身は phpinfo で、PHPの版、パス、読み込まれているモジュールが一覧になる。診断用に置いたまま残っていたので、ファイルを退避し、同じ場所は 403 にした。標準のドキュメントルート自体も、中身を見せない設定に変えた。
あわせて、各サイトの .env と web.config を、外からステータスコードだけ確認した。いずれも 403 か 404 で、本文は出ていなかった。ここは塞がっていることの確認で、追加の拒否は入れていない。
HTTPメソッドは、サイトに必要なものだけ残した。通常の閲覧やフォーム送信では使わないメソッドは、Apache側で拒否している。

2. 使っていないサイトと、名前だけのホスト
動いていないサイトは、ドキュメントルート、バーチャルホスト、不要なデータベースごと退役させた。残すサイトのデータベースとユーザーは触っていない。
DNSのワイルドカードは残した。* がある限り、消した名前も、存在しないサブドメインも、このサーバへ名前解決される。そこで、知っているホスト以外の HTTPS は 403 にした。知らない名前が、サーバ標準の証明書で応答する状態を止めるためだ。
公開中のサイトは、作業の前後で 200 であることと、証明書の発行者・期限を確認した。

3. アプリ側の入口
EC-CUBE は、系列内のセキュリティリリースへ上げた。対象は管理画面の多要素認証まわりだ。本番は短いメンテナンスに入れ、データベースを取り、公式差分を当て、スキーマを更新してからメンテナンスを解除した。バージョン表記、関連ファイル、フロントと管理ログインの応答を確認して完了にした。住所や帳票などのカスタマイズは残している。
WordPress は、本体もプラグインも、すでに当時の最新だった。追加したのは xmlrpc.phpの拒否だけだ。ピンバックや総当たりの通り道として残りやすいので、使っていなかったパスは 403 にした。
EC-CUBE の管理画面は、許可する接続元が空だった。ここへ作業用回線のIPだけを入れた。別の回線から入る日は、そのIPを足してキャッシュをクリアする。
認証なしで置いてあった作業用の一時PHPも、同じ回線だけに限定した。コマンドを実行するファイルではなかったが、内部のパスや作業の進み具合が見える。用が済んだら消す。


4. バージョンをヘッダに出さない
PHP は初期状態だと、応答ヘッダに X-Powered-By: PHP/x.x.x を付ける。expose_php=Offにして php-fpm を再読み込みすると、このヘッダが無くなる。画面の動作は変わらない。脆弱性そのものを塞ぐ設定ではなく、版の当たりを付けにくくする設定だ。
expose_php = Off
5. 繰り返しは fail2ban に任せる
fail2ban を入れ、ログ上で失敗や漁りが続くIPを、firewalld で一時的に DROP するようにした。有効にしたのは次の6つだ。
| Jail | 見ているもの |
|---|---|
| sshd | SSHのログイン失敗 |
| apache-auth | Basic認証などの失敗 |
| apache-badbots | 既知の悪質なクローラ |
| apache-noscript | 存在しないスクリプトへの漁り |
| apache-overflows | 異常に長いURLなど |
| recidive | 何度も遮断されたIPを、長めに遮断 |
作業用回線は ignoreip に入れ、自分の操作で自分を落とさないようにした。
副作用は、正規の利用者まで一時的に届かなくなることだ。会社やキャリアの出口IPは、無関係な人と共有されていることがある。導入後は fail2ban-client status で件数を見て、おかしい jail は条件を弱めるか外す。
01
🚪
失敗が少ない
まだ通す
02
⚠️
短時間に繰り返す
同じ送信元が続く
03
🔒
しばらく閉める
一時的に DROP
6. OSのセキュリティ更新だけ自動化する
dnf-automatic を入れ、セキュリティ更新だけ自動適用にした。カーネル更新のあとの再起動は自動にしない。全体を自動更新するより適用範囲は狭いが、意図しない再起動でサイトが落ちることを避けた。
7. よく来るIPは補助で落とす
直近のログで突出していた送信元だけ、SiteGuard の接続拒否と firewalld の DROP の両方に入れた。国単位の拒否は入れていない。海外からの正規アクセスがあるためだ。
これは初回の補助である。その後に同じアドレスがログへ残り続けても、BLOCK 済みなら、一件ずつ DROP 一覧へ写す作業はしていない。本命は公式シグネチャと、その自動更新のままにしてある。同じキャンペーンでも送信元はすぐ替わる。
公開されている悪評IPの一覧も、そのまま全面適用はしていない。古いアドレスや、クラウドの共有レンジで、正規の通信まで巻き込むことがある。自サーバのログで、明らかにスキャンと言えるものだけを手で足す、で足りている。
8. SiteGuardの管理ポートは作業用回線だけ
SiteGuard の管理画面は 9443番で待ち受けている。これを、作業用回線と localhost だけに開いた。それ以外からは接続がタイムアウトする。公開サイトの 80 番と 443 番はそのままだ。
この画面は、本番サイトの Let’s Encrypt とは別の証明書を使っている。IPを直指定して開くと、ブラウザは証明書の警告を出す。許可した回線から詳細へ進んで開けばよい。
外出先からは、SSHのローカル転送で届ける。
ssh -L 9443:127.0.0.1:9443 user@your-server
手元のブラウザでは https://127.0.0.1:9443/ を開く。警告は出やすい。作業用回線のIPが変わったら、firewalld の許可を更新する。この更新を忘れると、自分だけが入れなくなる。



見送ったこと
次の3つは、検討したうえで見送った。
IPv6の追加の締め
このVPSには、当時 IPv6 アドレスが付いていなかった。DNSの AAAA は残している。名前は IPv6 に解決されるが、サーバは IPv6 では応答しない。アドレスを有効にする直前に、firewalld で SSH、HTTP、HTTPS 以外が閉じていることを確認すれば足りる、と判断した。
検知ログからの自動DROP
WAFがすでに拒否しているリクエストを、件数で firewalld へ流し込む仕組みだ。共有IPや、決済・APIのクラウドアドレスを巻き込みやすい。通知だけにする段階を飛ばして自動で落とすのは、今の量に対して副作用が大きい。
別の複数台のVPSと、攻撃IPを常時同期すること
近い時間に、同じキャンペーンが複数台へ来ることはある。ただ送信元は使い捨てが多く、誤って落としたときの影響が台数ぶん広がる。各台の SiteGuard に BLOCK を任せ、突出したものだけ、その台のログを見て手で足す方が扱いやすい。
今回で一区切り
入れたもの
- 公開しなくてよいファイルと、未知のホストを閉じた
- アプリを更新し、管理画面は作業用回線だけにした
- fail2ban と、OSのセキュリティ更新を入れた
- 管理ポートと、突出した送信元だけを補助で落とした
きっかけが来てから
そのとき足す
- IPv6 を有効化する日
- 封鎖後も、同じ送信元が異常に増える日
- 本番の管理URLだけを突かれ続ける日
- 別の複数台で、同じキャンペーンが同時に目立つ日
戻せるようにしておく
触る設定ファイルは、作業の前に日時付きでコピーした。アプリの更新では、データベースのダンプを別のディレクトリへ残した。
公開サイトは、変更のたびに次を確認する。
- トップが 200 か
- 証明書の発行者と期限
- アプリの管理画面が、許可した回線からのみ開くか
- 環境変数ファイルが 403 か 404 のままか
その後の見方
実装はここで一区切りにした。続けているのは、次の3つだ。
- 作業用回線のIPが変わったら、SiteGuard の管理ポートと、アプリの管理画面の許可を更新する
- SiteGuard の検知を、週に一度見る。新しい種類のURLが増えていないかを確認する
- 本番の管理URLだけを狙われ続ける、DROP したあとも同じIPが異常に増える、実害が出る、のどれかが起きたら、そのとき足す
初回に突出していた送信元は入れてあり、それ以外は公式シグネチャの BLOCK に任せている。ログに多く出ている行を、そのたびに人手で写す作業はしていない。
チェックリスト
他のVPSへ持ち出すときの順は、こうなる。
- 確認したログの判定が BLOCK であることを見る
- phpinfo と、標準ドキュメントルートの公開を止める
- 環境変数ファイルが外から読めないことを、ステータスコードで確認する
- 使っていないホストを消し、ワイルドカード向けに、未知のホストを 403 にする
- アプリを系列内のセキュリティリリースへ上げ、使っていない
xmlrpc.phpを拒否する - アプリの管理画面に、許可するIPを入れる
expose_php=Offにする- fail2ban を入れ、作業用回線は対象から外す
- OSはセキュリティ更新のみ自動にし、再起動は手動のままにする
- 突出した送信元だけ、SiteGuard と firewalld に足す
- SiteGuard の管理ポートは、作業用回線と localhost だけにする
- 反映後に、公開サイトの応答と証明書を確認する
入口をゼロにはできない。新しい送信元、未知の攻撃、作業用回線の変化は残る。今回の効果は、偵察の材料と使っていない入口を減らし、すり抜けたときの範囲を、更新と権限の少なさで受け止めるところにある。