WPセキュリティ:wp-adminのセキュリティ|ログインURL変更・ログイン試行制限・XML-RPC

公正取引委員会の広告文 Ver 3.1
公正取引委員会の広告文 Ver 3.2(モバイル専用)

wp-adminのセキュリティは、WordPressの管理画面とログイン画面から入ってくる総当たり攻撃を防ぐ設定です。WordPressサイトを複数運営しているNginx + Apache構成のサーバーのアクセスログを確認したところ、ログインURLを変えた後もxmlrpc.phpへの自動リクエストが続いていたため、サーバー側の速度制限まで合わせて適用しました。

1️⃣ wp-adminとwp-login.phpが狙われる理由

WordPressの管理画面のURLが知られていることは弱点ですが、セキュリティを強化して補えます。すべてのWordPressサイトが同じ/wp-admin/と[wp-login.php]を使い、ログアウトした状態で/wp-admin/を開くと[wp-login.php]のログイン画面へ移動します。

攻撃は、このログイン画面にユーザー名とパスワードを繰り返し入れてみる総当たりで入ってきます。そのためwp-adminのセキュリティは、URLを隠すことよりログイン試行を止めることが先です。

WordPressのログイン入口は2つ
ブラウザーでアクセス
/wp-admin/
ログアウト状態ならログイン画面へ移動(302)
wp-login.php
ログイン画面・ユーザー名とパスワードを入力
アプリ・自動リクエスト
xmlrpc.php
ログイン画面なしでPOSTリクエストでユーザー名とパスワードを送る
ログイン画面を通らない
system.multicallで1回のリクエストに複数のパスワードを試す・ログインURLを変えてもアドレスはそのまま
同じユーザー名・パスワードの確認
どちらの入口もWordPressの同じ認証関数でアカウントを確認
総当たり攻撃は2つの入口のうち開いているほうから入ってきます。ログインURLの変更は上の入口にしか効かないため、xmlrpc.phpは別に閉じるかリクエスト数を制限します。

2️⃣ ログインURL変更の効果と限界

セキュリティプラグインでログインURLを変えると、既定の[wp-login.php]は404を返すようになり、既定のURLをそのまま叩く自動攻撃はログイン画面までたどり着けません。変えたURLは管理者アカウントを持つ人に別途伝える必要があります。

🧭 WordPressのログイン入口
解説ダイアグラム
⬜ プラグインなし
🛡️ Hide My WP
🚪 ログイン入口
/wp-login.php · /wp-admin/すべてのWordPressで同じアドレス
ログイン画面404
/newlogin変えたアドレス・管理者だけが知る
ログイン画面
POST /xmlrpc.phpログイン画面なしでパスワードを確認
パスワード確認に応答無効 405
テストサイトで実測・ログアウト状態のリクエスト
→
🤖 自動攻撃
知られたアドレスでパスワードを試せる入口
2
1
なし
プラグインがないと、すべてのWordPressサイトと同じ[wp-login.php]と、ログイン画面なしでパスワードを確認する[xmlrpc.php]の2か所でパスワードを試せます。
ログインURLを変えると[wp-login.php]と/wp-admin/は404を返しますが、[xmlrpc.php]はユーザー名とパスワードが正しいかをそのまま答えます。
XML-RPCまでオフにすると、[xmlrpc.php]へのログインリクエストはXML-RPCサービスが無効だという応答で終わり、知られたアドレスではパスワードを試せません。

この方法は手間が少ないですが、変えたURLを忘れると管理画面に入れず、FTPやファイルマネージャーでそのプラグインのフォルダー名を変えて無効化する必要があります。複数人で管理するサイトなら、アドレスを1か所に記録しておくのが適切です。

3️⃣ ログイン試行制限と2段階認証

ログインURLを変えても、新しいアドレスが知られればパスワードを当てる試みはそのまま続きます。WordPressのコアにはログイン失敗回数の制限がなく、間違ったパスワードを何度入れても同じログイン画面がまた開きます。

🧭 ログイン試行制限と2段階認証
解説ダイアグラム
⬜ プラグインなし
🛡️ セキュリティプラグイン
🔑 同じIPからのログイン
1回目間違ったパスワード
ログイン画面がまた開く残り3回
2回目間違ったパスワード
ログイン画面がまた開く残り2回
3回目間違ったパスワード
ログイン画面がまた開く残り1回
4回目間違ったパスワード
ログイン画面がまた開く20分ブロック
5回目ブロック中間違ったパスワードログイン・XML-RPCリクエスト
ログイン画面がまた開くxmlrpc.phpも403
パスワード入力正しいパスワード
コード入力画面
Authentication Code認証アプリの6桁・30秒ごとに変わる
管理画面
パスワードだけを知った攻撃者コードを知らない
コード画面で止まる
テストサイトで実測・同じIPから連続リクエスト
→
🤖 自動攻撃
パスワードを試し続けられるか
制限なし
4回目でブロック
コードで止まる
プラグインがないと、同じIPから間違ったパスワードを6回入れても毎回同じログイン画面がまた開きます。
ログイン試行制限をオンにすると、間違えるたびに残り回数が表示され、4回目に間違えるとそのIPは20分間ログインできません。ブロック中はxmlrpc.phpへのリクエストも403で終わります。
2段階認証をオンにすると、パスワードが正しくても、認証アプリが30秒ごとに新しく作る6桁の[Authentication Code]を入れないと管理画面が開きません。

ログイン試行制限プラグインは決められた回数を超えたIPを一定時間ブロックし、2段階認証はパスワードが正しくてもスマートフォンの認証アプリのコードをもう一度求めます。2段階認証をオンにするときは、スマートフォンをなくしたときに使うバックアップコードも一緒に保管します。

4️⃣ xmlrpc.phpを閉じる

[xmlrpc.php]はログイン画面を通らずにユーザー名とパスワードを受け取る2つ目の入口で、[system.multicall]方式を使うと1回のリクエストで複数のパスワードを試せます。

WordPressサイトを複数運営しているNginx + Apache構成のサーバーのアクセスログを見ると、10月13日0時から9時50分までに[xmlrpc.php]へのPOSTリクエストが2,250件ありました。リクエストは22個のIPから届き、User-AgentはJetpackとWordPress.comを装っていました。

WordPressのモバイルアプリやJetpackを使っていないなら、Nginxの設定で[xmlrpc.php]へのリクエストをブロックするか、セキュリティプラグインのXML-RPCオフ設定を使います。

# serverブロック内 — xmlrpc.phpへのリクエストを拒否
location = /xmlrpc.php {
    deny all;
}

この設定を入れると、[xmlrpc.php]へのGETとPOSTリクエストはPHPまで行かずにすべて403で終わります。セキュリティプラグインのXML-RPCオフはログインが必要なリクエストだけを拒否し、ファイルは応答し続けるため、PHPの実行まで減らすならNginxでブロックするのが適切です。

5️⃣ サーバーでブロック:管理者アクセスのIP制限・速度制限

固定IPからだけ管理するなら、Nginxで[wp-login.php]と/wp-admin/をそのIPだけに許可する方法がいちばん確実です。自宅や事務所のIPがよく変わる環境には向きません。

/wp-admin/全体をブロックすればよさそうですが、訪問者向けの検索やカートのような機能が[admin-ajax.php]へリクエストを送ることが多いため、そのファイルは開けておく必要があります。

# serverブロック内 — PHP処理のlocationより上に
location = /wp-login.php {
    allow 203.0.113.10;   # 管理に使う固定IP
    deny all;
    # この下にPHP処理locationの設定をそのまま移す
}

指定したIP以外から[wp-login.php]を開くと403が出て、指定したIPではログイン画面が開きます。location内にはサイトのPHP処理設定(fastcgi_passなど)をそのまま移さないと、ログイン画面が正常に動きません。

IPを固定できないなら、ログインしていない訪問者のPHPリクエストに速度制限をかける方法があり、1分に30回に制限して自動リクエストがサーバー資源を占有できないようにした過程は php-fpm max_children 警告 の記事にあります。

# httpブロック — ログインCookieのないリクエストだけをIPごとに数える
map $http_cookie $php_anon_key {
    default                 $binary_remote_addr;
    "~*wordpress_logged_in" "";
}
limit_req_zone $php_anon_key zone=php_anon:10m rate=30r/m;

# PHP処理のlocation内
limit_req zone=php_anon burst=60 nodelay;
limit_req_status 429;

ログインしていないIP 1つがPHPリクエストを続けて送ると、61回目までは処理され、その後は429が返され、ログインCookieのあるリクエストは数えません。ページキャッシュがPHPを通らずに応答する構成なら、一般の訪問者のページ閲覧はこの制限にかかりません。

ℹ️ 提携について
当サイトのコンテンツにはアフィリエイトリンクが含まれています。訪問者がこのリンクを通じて商品やサービスを購入すると、当サイトは販売元から手数料を受け取ります。この過程において、購入者が支払う金額(イベント割引適用で金額が下がります↓)は上昇しません。掲載されている価格・割引・在庫情報は作成時点のものであり、実際とは異なる場合がありますので、ご購入前に販売元にて最終確認をお願いいたします。商品の選定および評価は独自の基準に基づいて行われており、手数料の支払いの有無は紹介順や評価内容に影響を与えません。

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

人間であることを証明してください: 2   +   10   =