
ブルートフォース(総当たり)ログイン対策の基本|一人運用の4ステップ
障害の調査ついでに、何気なくログを開いた。
そこに、見覚えのないIPアドレスからのログイン失敗が、何百行も並んでいた。 admin、test、root。ユーザー名を変えながら、数秒おきに、ずっと。
一瞬、背筋が冷たくなりますよね。「もう入られているのでは」「今すぐ全部塞がないと」。でも、深呼吸して大丈夫です。インターネットに公開しているログイン画面やSSHには、こうした機械的な試行がほぼ常に来ています。 ログに並んでいること自体は、あなたの守りが甘い証拠ではありません。
大事なのは、慌てて全部を一度に変えないことです。とくに一人運用では、焦って入れた対策で自分が締め出されるほうが、よほど痛い事故になります。
結論:ブルートフォース(総当たり)ログイン対策は、次の4ステップで進めれば十分です。①まず数える(どこに・どれくらい来ているか、成功が混ざっていないか) → ②入口を絞る(管理画面・SSHに外から届く範囲を減らす) → ③回数を制限する(同じ相手からの連続失敗を遅らせる・一時的に弾く) → ④2つ目の鍵を足す(二要素認証・鍵認証)。いきなり④や「アカウントロック」から入るより、①で現状をつかんでから順に足すほうが、事故なく早く終わります。
なお、設定の書き方や画面の名前は、使っているOS・Webサーバー・CMS・クラウドの種類やバージョンでかなり違います。ここで扱うのは進め方の型です。実際の設定は、必ず自分の環境の公式ドキュメントと検証環境で確かめてください。
何が起きているか:総当たりには3つの型がある
「総当たり」とひとまとめに呼ばれがちですが、ログを見ると、だいたい次の3つの型に分かれます。どれが来ているかで、効く対策が少し変わります。
- ブルートフォース(純粋な総当たり):1つのユーザー名に対して、パスワードを次々に試す型。同じIPから短時間に大量の失敗が並びます。回数制限がよく効きます。
- パスワードスプレー:よくあるパスワード1つを、たくさんのユーザー名に少しずつ試す型。1アカウントあたりの失敗は少ないので、アカウント単位のロックでは引っかかりにくいのが特徴です。
- リスト型攻撃(クレデンシャルスタッフィング):どこか別のサービスから漏れたID・パスワードの組をそのまま試す型。1回で正解されることがあるので、回数制限だけでは防げません。ここは二要素認証や、パスワードの使い回しをなくす話になります。
つまり、回数制限は大事だけれど、それだけでは足りない。かといって、二要素認証だけでもログやサーバーの負荷は減りません。だから4ステップを重ねていきます。どれか1つで完璧にする必要はなく、重ねるほど効くと考えてください。
ステップ1:まず数える(ここが一番大事です)
対策を決める前に、いま何が起きているかを数えます。15分で終わります。見るのは3つだけです。
- どの入口に来ているか(SSH、管理画面、問い合わせフォームのログイン、APIなど)
- どれくらいの量か(1日あたり何回、何種類のIPから)
- 成功が混ざっていないか(ここだけは必ず見る)
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:入口を絞る(外から届く範囲を減らす)

いちばん効くのは、そもそも試行が届かないようにすることです。届かなければ、数える必要も、回数を数える必要もなくなります。
- 管理画面:触る場所が決まっているなら、IP制限が一番強い手です。どこからでも触るなら、手前にBasic認証をもう1枚。具体的な書き方と落とし穴は管理画面のアクセス制限|IP制限とBasic認証にまとめています。
- SSH:パスワード認証をやめて鍵認証にするだけで、総当たりはほぼ意味を失います。順番はSSHを安全にする基本|鍵認証とrootログイン禁止をどうぞ。クラウドならセキュリティグループやファイアウォールで、SSHを特定のIPからだけに絞るのも有効です。
- 使っていない入口:WordPressの
xmlrpc.phpのように、使っていないのに開いている入口が試行の的になっていることがあります。止めてよいかの確かめ方は不要なポート・サービスを閉じる棚卸し手順と同じ考え方で進められます(スマホアプリや外部連携が使っていないかは、止める前に必ず確認してください)。
ここでひとつだけ、順番の約束をさせてください。新しい入口で入れることを確かめてから、古い入口を閉じる。 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回失敗したらアカウントをロック」は分かりやすい対策ですが、一人運用では少し注意がいります。
- 攻撃者がわざと失敗させれば、正規の利用者がロックされる。社長のアカウントが毎朝ロックされていて、解除の問い合わせが毎日来る、ということが起こり得ます。
- パスワードスプレーには効きにくい。1アカウントあたりの失敗が少ないためです。
そのため、OWASPやNISTのガイドラインでも、連続失敗の回数は制限しつつ、永久ロックではなく一時的なロックや待ち時間の延長で、正規の利用者を締め出しすぎない工夫が挙げられています。自分で設定できるなら、「数回失敗したら数分待たせる、続けば待ち時間を延ばす」くらいの段階的な形が、現場では扱いやすいです。
プロキシ・CDN・ロードバランサーの後ろにいる場合
ここが一番つまずきやすいところです。サーバーの手前にCDNやロードバランサーがあると、サーバーから見える送信元IPが、全員「CDNのIP」になることがあります。
その状態でIPごとの回数制限やfail2banを入れると、利用者全員がひとまとめに数えられて、まとめて弾かれるおそれがあります。入れる前に、アクセスログの送信元IPが本当の利用者のIPになっているかを確認してください。なっていなければ、本当の送信元を受け取る設定(nginxの real_ip 系の設定など)を先に整えるか、CDNやWAF側の機能で制限をかけるほうが確実です。
ステップ4:2つ目の鍵を足す
回数制限は「たくさん試す相手」には効きますが、漏れたパスワードで1回目に正解されるリスト型攻撃は止められません。ここを受け止めるのが2つ目の鍵です。
- 管理画面には二要素認証を。 締め出されないための準備(予備コード・予備の管理者)を先に整える順番は、管理画面に二要素認証(2FA)を入れる手順で詳しく扱っています。
- SSHは鍵認証に。 ステップ2と同じです。パスワードで入る入口が残っていなければ、総当たりの試行はすべて空振りになります。
- パスワードそのものも見直す。 記号の組み合わせを強制するより、十分な長さと、使い回しをしないことが効きます。NISTのガイドラインでも、長さを重視し、漏洩が知られているパスワードとの照合を勧める方向になっています。保管の仕方はパスワードとAPIキーの平文放置をなくす保管手順をどうぞ。
具体例:「ログが真っ赤」から3日で落ち着いた現場
会社サイト(WordPress)と社内の受注システムを、ほかの業務と兼務で守っている担当者の例で、順番をたどってみます。
- きっかけ:サーバーが少し重い日があり、アクセスログを開いたら
wp-login.phpへのPOSTが大量に並んでいた。 - 1日目(数える・15分):直近24時間で、
wp-login.phpへのPOSTが約1,200回、送信元IPは約300種類。1IPあたり平均4回なので、1つのIPを弾いても効かない分散型だと分かった。xmlrpc.phpにも約800回。SSHのAcceptedと管理画面の最終ログイン履歴を確認し、心当たりのない成功はなかった。 - 1日目(絞る・30分):
xmlrpc.phpは、スマホアプリや外部連携で使っていないことを確認してから止めた。管理画面は社内と自宅の回線からしか触らないので、wp-login.phpと/wp-admin/にIP制限をかけた。自分のスマホのテザリングから入れないことに気づき、Basic認証との併用に切り替えた。 - 2日目(回数制限):受注システムの会員ログインは誰でも使うので、IP制限はできない。手前にロードバランサーがあり、ログの送信元IPがすべて同じだったため、先に本当の送信元を記録する設定を整えてから、ログインURLだけ
limit_reqをゆるめにかけた。1週間ログを見て、正規の利用者が429になっていないことを確かめた。 - 3日目(2つ目の鍵):WordPressの管理者2名に二要素認証を入れた。予備コードは印刷して金庫へ。
- 記録:変更管理台帳に、止めた入口・制限の値・解除の方法を1行ずつ残した。
1週間後、wp-login.php への試行はIP制限とBasic認証の手前で止まり、WordPress本体まで届く失敗ログはほぼなくなった。サーバーが重い日も、それ以来出ていない。
この例で効いたのは、高度な設定ではありません。1日目に「分散型だ」と数字で分かったことで、IPを1つずつ弾く作戦に時間を使わずに済みました。数えるだけで、打つ手が半分に絞れます。
影響:対策すると、何が変わるか
- ログが読めるようになる。 失敗ログが減ると、本当に見たい障害のログが埋もれなくなります。地味ですが、これがいちばん毎日効きます。
- サーバーの負荷が下がることがある。 ログイン処理は、パスワードの照合で意外と重い処理です。試行が手前で止まれば、そのぶん本体が軽くなります。
- 「もし入られていたら」という不安が小さくなる。 何を、どこまでやったかを説明できる状態は、それだけで安心につながります。点検や取引先のチェックシートにも答えやすくなります。
一方で、正直に見込んでおきたい手間もあります。IP制限は出張先や在宅から入れなくなることがある。回数制限は、値によっては正規の利用者を弾くことがある。だから、ゆるめに入れてログを見てから締める。この順番が、一人運用でいちばん安全です。
明日やること:まず「数える」だけ
設定は変えなくて大丈夫です。明日は次の順でどうぞ。
- ログイン画面とSSHの入口を書き出す。 管理画面、会員ログイン、SSH、使っていないかもしれない
xmlrpc.phpのような入口。5分で十分です。 - それぞれの失敗の数を、直近24時間分だけ数える。 上のコマンドで構いません。
- 成功ログイン(
Acceptedや管理画面の最終ログイン履歴)に、心当たりのないものがないか見る。 ここだけは必ず。 - 送信元IPが本当の利用者のIPになっているか、ログで1行だけ確かめる。 CDNやロードバランサーの後ろかどうかが、ここで分かります。
- 数えた結果を、変更管理台帳かメモに1行残す。 対策の前後で比べるための「平常値」になります。考え方は監視の平常値(ベースライン)を把握すると同じです。
ここまでで今日は終わりでいいです。数えておけば、次にやるべきステップは自然に1つに決まります。
ブルートフォース(総当たり)ログイン対策チェックリスト
全部そろえる前提ではありません。上から優先順位で並べています。
まず数える
- 外から届くログインの入口(管理画面・会員ログイン・SSH・API)を書き出したか
- 入口ごとの失敗回数と、送信元IPの数を数えたか
- 心当たりのない成功ログインが混ざっていないか確認したか
- ログの送信元IPが、CDN・ロードバランサーのIPになっていないか確認したか
入口を絞る
- 触る場所が決まっている管理画面に、IP制限(またはBasic認証)をかけたか
- SSHのパスワード認証をやめ、鍵認証にしたか(新しい入口で入れることを先に確かめたか)
- 使っていないログインの入口(
xmlrpc.phpなど)を、利用がないことを確かめてから止めたか
回数を制限する
- ログインURLに速度制限(
limit_reqなど)をゆるめにかけ、正規の利用者が弾かれていないか見たか - fail2ban などを入れる場合、自分の接続元を除外(
ignoreip)したか - 誤って弾いたときの解除方法を、手元のメモに控えたか
- アカウントロックを入れる場合、永久ではなく一時的・段階的にしたか
2つ目の鍵を足す(落ち着いたら)
- 管理者アカウントに二要素認証を入れたか(予備コードを先に用意したか)
- パスワードの使い回しがないか、十分な長さか見直したか
- 止めた入口・制限の値・解除の方法を、変更管理台帳に残したか
「まず数える」の4つが埋まっていれば、もう焦る段階ではありません。残りは1つずつで大丈夫です。

ログに並んだ失敗の行を見て、ぞっとしたのは、それだけ真剣に守っているからです。気づかなければ、ぞっとすることもありません。気づいた時点で、もう一歩目は踏み出しています。
総当たりの試行は、明日もたぶん来ます。それはあなたのせいではありません。ただ、入口を1つ絞るたびに、届く数は確実に減っていきます。
明日は、ログイン失敗を数えるところから始めてみませんか。数字が出ると、不安は「やること」に変わります。
ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。
よければ、こちらも
- 管理画面のアクセス制限|IP制限とBasic認証の基本と使い分け:ステップ2の具体的な書き方です。プロキシの後ろでの落とし穴も扱っています。
- 管理画面に二要素認証(2FA)を入れる|一人運用で締め出されない手順:ステップ4。リスト型攻撃への一番の備えです。
- SSHを安全にする基本|鍵認証とrootログイン禁止の始め方:SSHへの総当たりを空振りにする一手です。
- 不正アクセスの疑いがあるときの初動と保全|まず止める順番:心当たりのない成功ログインを見つけたら、こちらを先に。
- 大量のログからエラーを絞り込む|grep・tail・lessの実務術:ステップ1で数えるときの手元の道具です。