深夜のデスクで、ログイン失敗のログが延々と流れるモニターを見つめ、どこから手を付けるか考えている一人運用の保守担当者

ブルートフォース(総当たり)ログイン対策の基本|一人運用の4ステップ

障害の調査ついでに、何気なくログを開いた。

そこに、見覚えのないIPアドレスからのログイン失敗が、何百行も並んでいた。 admin、test、root。ユーザー名を変えながら、数秒おきに、ずっと。

一瞬、背筋が冷たくなりますよね。「もう入られているのでは」「今すぐ全部塞がないと」。でも、深呼吸して大丈夫です。インターネットに公開しているログイン画面やSSHには、こうした機械的な試行がほぼ常に来ています。 ログに並んでいること自体は、あなたの守りが甘い証拠ではありません。

大事なのは、慌てて全部を一度に変えないことです。とくに一人運用では、焦って入れた対策で自分が締め出されるほうが、よほど痛い事故になります。

結論:ブルートフォース(総当たり)ログイン対策は、次の4ステップで進めれば十分です。①まず数える(どこに・どれくらい来ているか、成功が混ざっていないか) → ②入口を絞る(管理画面・SSHに外から届く範囲を減らす) → ③回数を制限する(同じ相手からの連続失敗を遅らせる・一時的に弾く) → ④2つ目の鍵を足す(二要素認証・鍵認証)。いきなり④や「アカウントロック」から入るより、①で現状をつかんでから順に足すほうが、事故なく早く終わります。

なお、設定の書き方や画面の名前は、使っているOS・Webサーバー・CMS・クラウドの種類やバージョンでかなり違います。ここで扱うのは進め方の型です。実際の設定は、必ず自分の環境の公式ドキュメントと検証環境で確かめてください。

何が起きているか:総当たりには3つの型がある

「総当たり」とひとまとめに呼ばれがちですが、ログを見ると、だいたい次の3つの型に分かれます。どれが来ているかで、効く対策が少し変わります。

つまり、回数制限は大事だけれど、それだけでは足りない。かといって、二要素認証だけでもログやサーバーの負荷は減りません。だから4ステップを重ねていきます。どれか1つで完璧にする必要はなく、重ねるほど効くと考えてください。

ステップ1:まず数える(ここが一番大事です)

対策を決める前に、いま何が起きているかを数えます。15分で終わります。見るのは3つだけです。

  1. どの入口に来ているか(SSH、管理画面、問い合わせフォームのログイン、APIなど)
  2. どれくらいの量か(1日あたり何回、何種類のIPから)
  3. 成功が混ざっていないか(ここだけは必ず見る)

SSHなら、たとえばこう確認できます。

# Debian/Ubuntu 系:失敗の数を数える
grep "Failed password" /var/log/auth.log | wc -l

# RHEL 系は /var/log/secure。journald の環境なら
journalctl -u ssh --since "24 hours ago" | grep "Failed password" | wc -l
# (ユニット名が sshd の環境もあります)

# 失敗の多いIPの上位を見る
grep "Failed password" /var/log/auth.log | grep -oE "from [0-9.]+" | sort | uniq -c | sort -rn | head

# 成功したログインを確認する(ここが本命)
grep "Accepted" /var/log/auth.log

Webの管理画面なら、アクセスログからログインURLへのPOSTを数えます。WordPressなら wp-login.php や xmlrpc.php が典型です。

# ログインURLへのPOSTを、送信元IPごとに数える(ログ形式が標準の場合)
grep "POST /wp-login.php" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head

大量のログから目的の行を絞るコツはgrep・tail・lessの実務術にまとめています。

ここで、Accepted(成功)に心当たりのないIPや時間帯が混ざっていたら、話が変わります。対策の前に、不正アクセスの疑いがあるときの初動と保全を先に進めてください。逆に、失敗だけが並んでいるなら、いまは「試されているだけ」です。落ち着いて次へ進めます。

ステップ2:入口を絞る(外から届く範囲を減らす)

建物の入口に向かって外側から3つの関門が順に並び、外から入口を絞る門、回数を制限する係、2つ目の鍵の扉で守られていることを示した図
1つで完璧にしなくていい。外側から「入口を絞る」「回数制限」「2つ目の鍵」を重ねるほど、総当たりは届かなくなる

いちばん効くのは、そもそも試行が届かないようにすることです。届かなければ、数える必要も、回数を数える必要もなくなります。

ここでひとつだけ、順番の約束をさせてください。新しい入口で入れることを確かめてから、古い入口を閉じる。 SSHなら、鍵で入れるセッションを1本開いたまま、別のターミナルで設定を変えて試す。これを守るだけで、締め出しの事故はほぼ防げます。

ステップ3:回数を制限する(連続失敗を遅らせる・一時的に弾く)

入口を絞りきれない場所(会員のログイン画面など、誰でも使う入口)では、同じ相手からの連続失敗を制限します。方法は大きく3つあります。

Webサーバーで、ログインURLだけ速度を制限する

nginxなら limit_req で、ログインURLへのリクエストを「1分に何回まで」と制限できます。考え方はこうです。

# http ブロック:送信元IPごとに、1分あたり10回までのゾーンを定義
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;

# server ブロック内の、ログインURLの location に追記する
#(既存の PHP 等の設定はそのまま残し、この2行を足すイメージ)
limit_req zone=login burst=5 nodelay;
limit_req_status 429;

値は例です。最初はゆるめに設定して、ログで正規の利用者が弾かれていないかを見てから締めるのが安全です。設定を反映するときは設定ファイル変更を安全に反映する手順のとおり、nginx -t で構文を確かめてから reload してください。

fail2ban などで、失敗の多いIPを一時的に弾く

fail2ban は、ログを監視して「一定時間内に何回失敗したIP」を一時的にファイアウォールで弾くツールです。SSHにも、Webのログイン画面にも使えます。

# /etc/fail2ban/jail.local(jail.conf は直接編集せず、こちらに書く)
[DEFAULT]
# 自分や会社の固定IPは除外しておく(締め出し防止)
ignoreip = 127.0.0.1/8 ::1 203.0.113.10

[sshd]
enabled  = true
maxretry = 5
findtime = 10m
bantime  = 1h
# 状態の確認(いま何件弾いているか)
fail2ban-client status sshd

# 誤って自分を弾いたときの解除
fail2ban-client set sshd unbanip <IPアドレス>

ここで大事なのは2つです。ignoreip に自分の接続元を入れておくことと、解除のコマンドを手元のメモに控えておくこと。弾くのは機械なので、間違えることがあります。間違えたときに戻せる道を、先に用意しておきます。

アカウントロックは「一時的に」「段階的に」

「5回失敗したらアカウントをロック」は分かりやすい対策ですが、一人運用では少し注意がいります。

そのため、OWASPやNISTのガイドラインでも、連続失敗の回数は制限しつつ、永久ロックではなく一時的なロックや待ち時間の延長で、正規の利用者を締め出しすぎない工夫が挙げられています。自分で設定できるなら、「数回失敗したら数分待たせる、続けば待ち時間を延ばす」くらいの段階的な形が、現場では扱いやすいです。

プロキシ・CDN・ロードバランサーの後ろにいる場合

ここが一番つまずきやすいところです。サーバーの手前にCDNやロードバランサーがあると、サーバーから見える送信元IPが、全員「CDNのIP」になることがあります。

その状態でIPごとの回数制限やfail2banを入れると、利用者全員がひとまとめに数えられて、まとめて弾かれるおそれがあります。入れる前に、アクセスログの送信元IPが本当の利用者のIPになっているかを確認してください。なっていなければ、本当の送信元を受け取る設定(nginxの real_ip 系の設定など)を先に整えるか、CDNやWAF側の機能で制限をかけるほうが確実です。

ステップ4:2つ目の鍵を足す

回数制限は「たくさん試す相手」には効きますが、漏れたパスワードで1回目に正解されるリスト型攻撃は止められません。ここを受け止めるのが2つ目の鍵です。

具体例:「ログが真っ赤」から3日で落ち着いた現場

会社サイト(WordPress)と社内の受注システムを、ほかの業務と兼務で守っている担当者の例で、順番をたどってみます。

1週間後、wp-login.php への試行はIP制限とBasic認証の手前で止まり、WordPress本体まで届く失敗ログはほぼなくなった。サーバーが重い日も、それ以来出ていない。

この例で効いたのは、高度な設定ではありません。1日目に「分散型だ」と数字で分かったことで、IPを1つずつ弾く作戦に時間を使わずに済みました。数えるだけで、打つ手が半分に絞れます。

影響:対策すると、何が変わるか

一方で、正直に見込んでおきたい手間もあります。IP制限は出張先や在宅から入れなくなることがある。回数制限は、値によっては正規の利用者を弾くことがある。だから、ゆるめに入れてログを見てから締める。この順番が、一人運用でいちばん安全です。

明日やること:まず「数える」だけ

設定は変えなくて大丈夫です。明日は次の順でどうぞ。

  1. ログイン画面とSSHの入口を書き出す。 管理画面、会員ログイン、SSH、使っていないかもしれない xmlrpc.php のような入口。5分で十分です。
  2. それぞれの失敗の数を、直近24時間分だけ数える。 上のコマンドで構いません。
  3. 成功ログイン(Accepted や管理画面の最終ログイン履歴)に、心当たりのないものがないか見る。 ここだけは必ず。
  4. 送信元IPが本当の利用者のIPになっているか、ログで1行だけ確かめる。 CDNやロードバランサーの後ろかどうかが、ここで分かります。
  5. 数えた結果を、変更管理台帳かメモに1行残す。 対策の前後で比べるための「平常値」になります。考え方は監視の平常値(ベースライン)を把握すると同じです。

ここまでで今日は終わりでいいです。数えておけば、次にやるべきステップは自然に1つに決まります。

ブルートフォース(総当たり)ログイン対策チェックリスト

全部そろえる前提ではありません。上から優先順位で並べています。

まず数える

入口を絞る

回数を制限する

2つ目の鍵を足す(落ち着いたら)

「まず数える」の4つが埋まっていれば、もう焦る段階ではありません。残りは1つずつで大丈夫です。

対策を入れ終えて、静かになったログ画面を見ながら穏やかにコーヒーを飲んでいる保守運用担当者

ログに並んだ失敗の行を見て、ぞっとしたのは、それだけ真剣に守っているからです。気づかなければ、ぞっとすることもありません。気づいた時点で、もう一歩目は踏み出しています。

総当たりの試行は、明日もたぶん来ます。それはあなたのせいではありません。ただ、入口を1つ絞るたびに、届く数は確実に減っていきます。

明日は、ログイン失敗を数えるところから始めてみませんか。数字が出ると、不安は「やること」に変わります。

ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。

よければ、こちらも

関連用語