VPSサーバーのセキュリティログを
確認して攻撃を検出する方法
VPSを運用していると、ポートスキャン、WordPress脆弱性スキャン、RCE攻撃など、日常的にさまざまな攻撃を受けます。
ログを定期的に確認し、防御が正しく機能しているか検証することが重要です。
本記事では、実際のサーバーログを使って攻撃の検出方法と対処法を解説します。
こんな人向けの記事です
- VPSサーバーを運用していて、攻撃を受けていないか確認したい
- Nginxアクセスログ・エラーログの読み方を知りたい
- UFWやFail2Banのログから攻撃状況を把握したい
- ポートスキャンやWebスキャンへの対処法を知りたい
確認すべきログファイル一覧
サーバーのセキュリティ状態を把握するために、以下のログを定期的に確認します。
| ログファイル | 内容 | 確認頻度 |
|---|---|---|
/var/log/nginx/access.log | 全HTTPアクセス記録 | 月次 |
/var/log/nginx/error.log | Nginxエラー(Rate Limit超過等) | 月次 |
/var/log/kern.log | UFWのブロックログ | 月次 |
/var/log/auth.log | SSH認証の成功・失敗 | 月次 |
| Djangoセキュリティログ | XSS/SQLi等の攻撃検知 | 月次 |
Nginxアクセスログの分析
IP別アクセス数ランキング
まず、どのIPから大量アクセスがあるかを確認します。
cat /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
出力例:
3199 185.177.72.52
2242 185.177.72.38
1639 185.177.72.60
644 150.31.5.3 ← 自社IP(正常)
476 68.221.64.54
354 40.80.90.214
HTTPステータスコード別集計
404が大量に発生していれば、存在しないパスへのスキャンを受けています。
cat /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c | sort -rn
10712 404 ← 大量のスキャン
392 444 ← Nginxが接続を切断(不明ドメイン等)
257 503 ← Rate Limit超過
196 403 ← アクセス拒否
117 200 ← 正常アクセス
攻撃パターンの特定
よく見られる攻撃パターンをアクセスログから抽出します。
grep -E "(admin|wp-login|\.env|phpinfo|\.php|shell|passwd|\.git)" /var/log/nginx/access.log | tail -20
WordPress脆弱性スキャン
WordPressを使っていなくても、以下のようなスキャンが日常的に来ます。
104.211.96.131 - "GET /wp-login.php HTTP/1.1" 404
104.211.96.131 - "GET /wp-admin/images/ HTTP/1.1" 404
104.211.96.131 - "GET /xmlrpc.php HTTP/1.1" 404
104.211.96.131 - "GET /phpMyAdmin/ HTTP/1.1" 404
環境ファイル・機密情報の探索
185.177.72.52 - "GET /.env HTTP/1.1" 404
185.177.72.52 - "GET /node_modules/.env HTTP/1.1" 404
185.177.72.52 - "GET /private.key HTTP/1.1" 404
185.177.72.52 - "GET /secrets.yml HTTP/1.1" 404
185.177.72.52 - "GET /settings.py HTTP/1.1" 404
RCE(リモートコード実行)攻撃
# Apache パストラバーサル (CVE-2021-41773)
81.17.103.232 - "POST /cgi-bin/.%2e/.%2e/.%2e/bin/sh HTTP/1.1" 400
# PHP RCE
217.217.251.46 - "POST /hello.world?auto_prepend_file=php://input HTTP/1.1" 444
# XDebug RCE
79.124.40.174 - "GET /?XDEBUG_SESSION_START=phpstorm HTTP/1.1" 444
# Exchange ProxyShell (CVE-2021-34473)
135.237.126.114 - "GET /autodiscover/autodiscover.json?@zdi/Powershell HTTP/1.1" 404
Nginxエラーログの分析
エラーログではRate Limit超過やSSLエラーを確認できます。
tail -100 /var/log/nginx/error.log
Rate Limit超過
[error] limiting requests, excess: 20.490 by zone "general",
client: 20.214.159.60, request: "GET /wp-content/index.php HTTP/1.1"
攻撃者がRate Limitを超過してブロックされている記録です。zone "general" は一般的なレート制限、zone "login" はログインページ用のより厳しい制限です。
SSL ハンドシェイクエラー
[crit] SSL_do_handshake() failed
(SSL: error:0A00006C:SSL routines::bad key share)
client: 216.180.246.175
不正なSSLハンドシェイクを試みるスキャナーです。TLSバージョンや暗号スイートの互換性がない場合に発生し、接続は確立されません。
自社IPのRate Limit超過に注意
ignoreipに追加しておきましょう。
[DEFAULT]
ignoreip = 127.0.0.1/8 自社のIPアドレス
bantime = 3600
findtime = 600
maxretry = 3
UFWログでポートスキャンを検出
UFW(Uncomplicated Firewall)のログで、許可ポート以外へのアクセス試行を確認します。
grep "UFW BLOCK" /var/log/kern.log | tail -30
[UFW BLOCK] IN=ens3 SRC=212.73.148.28 DST=219.94.232.146
PROTO=TCP SPT=48029 DPT=49678 SYN
[UFW BLOCK] IN=ens3 SRC=79.124.62.134 DST=219.94.232.146
PROTO=TCP SPT=57298 DPT=52829 SYN
[UFW BLOCK] IN=ens3 SRC=176.65.149.27 DST=219.94.232.146
PROTO=TCP SPT=56251 DPT=38194 SYN
ログの読み方:
| フィールド | 意味 |
|---|---|
SRC | 攻撃元IPアドレス |
DST | ターゲット(自サーバー)IP |
DPT | スキャンされたポート番号 |
SYN | TCP SYNパケット(接続開始要求) |
PROTO | プロトコル(TCP/UDP) |
よくあるスキャン元
| IP帯域 | 種別 |
|---|---|
| 212.73.148.x | 組織的ポートスキャン(複数IPから同時にスキャン) |
| 167.94.138.x / 167.94.146.x | Censys(インターネットスキャンサービス) |
| 79.124.62.x | 繰り返しスキャン(同一ポートを定期的にスキャン) |
| 176.65.149.x | SYNスキャン(高速ポートスキャン) |
| 147.185.132.x | GreyNoise系スキャナー |
Fail2Banのステータス確認
Fail2Banが正常に動作し、攻撃IPをBANしているか確認します。
# 全Jailのステータス
sudo fail2ban-client status
# 特定Jailの詳細
sudo fail2ban-client status nginx-limit-req
sudo fail2ban-client status nginx-botsearch
sudo fail2ban-client status django-security
sudo fail2ban-client status sshd
Status for the jail: nginx-botsearch
|- Filter
| |- Currently failed: 1
| |- Total failed: 7
| `- File list: /var/log/nginx/access.log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 185.177.72.52
確認すべきポイント:
- Currently banned — 現在BANされているIP数
- Total banned — 累計BAN数
- Banned IP list — BANされているIPの一覧
SSH認証ログの確認
不正なSSHログイン試行がないか確認します。
# 認証成功の履歴
last -20
# 認証失敗の履歴
sudo lastb -10
# 詳細ログ
tail -50 /var/log/auth.log
lastbの出力に自社IP以外が表示されなければ安全です。
侵入の形跡チェック
ログ確認に加えて、サーバー内部に侵入の形跡がないかもチェックします。
1. ログイン可能なユーザーの確認
# シェルが割り当てられたユーザー一覧
cat /etc/passwd | grep -v nologin | grep -v false
想定外のユーザーが追加されていないか確認します。
2. リスニングポートの確認
sudo ss -tlnp
意図しないポートでリッスンしているプロセスがないか確認します。
3. 不審なcronジョブの確認
# ユーザーのcrontab
crontab -l
# システムのcrontab
cat /etc/crontab
ls /etc/cron.d/
4. 不審なファイルの確認
# /tmp等に最近作成されたファイル
find /tmp /var/tmp /dev/shm -type f -newer /etc/passwd 2>/dev/null
# 想定外のPHPファイル(Webシェル等)
find / -name "*.php" -newer /etc/passwd -not -path "/proc/*" 2>/dev/null
発見した攻撃への対処法
大量スキャンボットのブロック
特定のIP帯域から大量スキャンがある場合、Nginxで直接ブロックします。
server {
listen 443 ssl;
server_name your-site.com;
# 悪意あるスキャナーのブロック
deny 185.177.72.0/24;
# ... 他の設定
}
設定後、Nginxをリロードします。
sudo nginx -t && sudo nginx -s reload
自社IPのFail2Ban除外
自社IPがRate Limitに引っかかってBANされないよう、ignoreipに追加します。
[DEFAULT]
ignoreip = 127.0.0.1/8 あなたのIP
bantime = 3600
findtime = 600
maxretry = 3
# 設定変更後、Fail2Banを再起動
sudo systemctl restart fail2ban
# ignoreipが反映されたか確認
sudo fail2ban-client get nginx-limit-req ignoreip
pingの無効化
pingに応答するとスキャナーにサーバーの存在を知らせてしまいます。無効化するには:
# 一時的に無効化
sudo sysctl -w net.ipv4.icmp_echo_ignore_all=1
# 永続化(再起動後も有効)
echo "net.ipv4.icmp_echo_ignore_all=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
まとめ
| 確認項目 | コマンド | 正常な状態 |
|---|---|---|
| IP別アクセス数 | awk + sort + uniq | 自社IP以外の大量アクセスがないこと |
| ステータスコード | awk + sort + uniq | 404が大量でも全てスキャン(200で機密情報を返していないこと) |
| ポートスキャン | grep "UFW BLOCK" kern.log | 全て「UFW BLOCK」で遮断されていること |
| Fail2Ban | fail2ban-client status | Jailが稼働中で攻撃IPがBANされていること |
| SSH認証 | last / lastb | 自社IP以外のログイン履歴がないこと |
| 侵入の形跡 | ss -tlnp / crontab / find | 不審なポート・ジョブ・ファイルがないこと |