xmlrpc.php攻撃の実録|総当たり2,706件、ログインIDは初期設定のまま外に出ていた

【PR】当ページのリンクには広告が含まれています。
xmlrpc.php攻撃のアイキャッチ。「IDは外に出ていた」の見出しと、ログイン失敗が2,706件あったことを示す管理画面風のパネル

「ログイン失敗:24時間で113件」。

2026年10月18日、WordPressのダッシュボードにそう出ていました。

WordPressのダッシュボードにそう出ていました。直近30日の平均は1日28件ほどです。いつもの4倍でした。

このブログで実際に起きたことです。その日のうちにログを読み、主な対策を済ませました。確認できたログの範囲では、侵入の形跡は見つかっていません。

ただ、攻撃者は筆者の本物のログインIDを知っていました。調べると、IDはWordPressの初期設定のまま、外から見える状態になっていました。

「xmlrpc.php 攻撃」で検索すると、無効化の手順がたくさん出てきます。どれも間違いではありません。ただ筆者のログには、無効化だけでは足りない理由もはっきり出ていました。

この記事では実際のログをもとに、攻撃の中身、IDが漏れる仕組み、漏れても入らせない守り方を説明します。

この記事でわかること
  • xmlrpc.phpへの総当たり攻撃が、実際にどんなペースと手順で来るか
  • 侵入されたかどうかを、ログイン履歴から見分けるポイント
  • WordPressのログインIDが、初期設定のままだと外から見える2つの経路
  • 「IDは漏れる前提」で組む、3層の守り方
  • 国外アクセス制限の思わぬ副作用(Googlebotと記事下の表示)

目次

結論:xmlrpc.phpを止めても、IDが漏れていれば別の入口から来る

先に結論です。xmlrpc.phpは、使っていないなら無効化しておきたい入口です。WordPressの公式ドキュメントにも、そう書かれています。

ただ、それで総当たりが終わるわけではありません。筆者のサイトに来たログイン失敗を、入口ごとに分けるとこうなりました。

入口失敗の件数xmlrpc.phpを止めると
xmlrpc.php(XML-RPC)1,600件止まる
ログインページ(wp-login.php)1,106件止まらない
合計2,706件4割はそのまま残る
まもる
xmlrpc.phpを止めれば、それで終わりだと思っていました
しろ
止めるのは正解だよ。ただログを入口ごとに分けたら、4割はログインページのほうから来ていたんだ

しかも、失敗の約3分の2は本物のログインIDで試されていました。IDを知っている相手は、入口を1つ閉じても、残った入口へ同じIDで来ます。

だから守りは、入口を減らす、IDを出さない、IDが漏れても入れない、の3層で考えます。いちばん大事なのは3つ目です。

xmlrpc.phpとログインページへの総当たり:8か月で2,706件、3つのパターン

材料は2つです。1つは、セキュリティプラグイン「XO Security」のログイン履歴(2026年2月20日〜10月18日)。もう1つは、Xserverのアクセスログ(10月18日の朝の約7時間分)です。

この期間のログイン失敗は2,706件でした。成功は1,290件です。接続元と時刻を照らし合わせた限り、成功はすべて筆者本人の操作か、自分のサイトからの通知でした。

試されたユーザー名は、次の通りです。

試されたユーザー名失敗の件数出どころ(筆者の推測)
本物のログインID1,757件著者ページのURLやREST APIで見える状態だった(後述)
表示名(ペンネーム)376件記事の著者欄
ドメイン名をそのままIDにしたもの375件サイトのURL
admin194件定番の推測
5桁の数字4件不明

本物のIDは、ログが残る2月の時点で、もう試されていました。来方を並べると、3つの型に分かれます。

パターン時期来方接続元
①低速で長く続くbot2月〜現在IPを替えながら、間隔をあけて少しずつ試すさまざま
②数か月おきの集中攻撃4月・5月・9月・10月1回あたり33〜103回。毎回「5桁の数字→admin→本物のID」の順国内の一般回線
③表示名を狙う短期集中6月〜現在2〜3分で40〜118回。本物のIDと表示名を交互に試すクラウド事業者の回線

①は、検知されにくいよう、わざとゆっくり来るタイプだと考えています。②は手順が毎回同じなので、同じツールが使われているのでしょう。端末の情報(ユーザーエージェント)は毎回ランダムに偽装されていました。「Safari/15.0.0.0 Safari/537.36」のような、実在しない組み合わせも混じっています。

厄介なのは、②が国内の一般回線から来ていたことです。Xserverの国外アクセス制限では、この型は止まりません。乗っ取られた端末やプロキシを経由している可能性が高いと考えています。

侵入されたかは「成功」の行で見分ける

失敗が何千件あっても、それだけでは侵入の証拠になりません。見るべきは成功の行です。

ISMS(ISO/IEC 27001:2022)の言葉では、ログイン試行は「事象」にあたります。インシデントかどうかは、評価して決めるものです(附属書AのA.5.25)。成功の行を確かめるのは、その最初の一歩だと考えています。事象とインシデントの区別は、ISMS連載のA.5.19〜A.5.30の回で書きました。

見る列本人のログイン疑うべきログイン
接続元(IP)自宅や携帯回線など、心当たりがある心当たりのない回線、海外、クラウド事業者
時刻自分が作業していた時間寝ていた時間、移動中
端末自分のブラウザやアプリ見慣れない端末。ただし偽装できるので、これだけで判断しない
ユーザー名自分のID空欄なら、自分のサイトからのピンバックのことがある

紛らわしいのが最後の行です。筆者のログにも、ユーザー名が空欄の「成功」がありました。端末の欄は「The Incutio XML-RPC PHP Library」です。WordPress自身が、自分の記事へのリンクを通知した記録(セルフピンバック)だと考えています。端末の欄のバージョン表記が、サイトの更新履歴と同じ順に変わっていたためです。接続元が自分のサーバーのIPアドレスと一致すれば、より確かめられます。

アクセスログには、秘密のファイル(.envや.git)や、乗っ取り済みのサイトに置かれがちなファイルを探すアクセスもありました。返っていたのはすべて404や403で、仕込まれたファイルは見当たりません。

ログインIDは、初期設定のまま外から見えていた

IDが外から見えていた経路は2つです。どちらも、WordPressを初期設定のまま使うと起きます。

経路何が見えるかなぜIDが出るか
著者ページのURL(著者アーカイブ)「/author/投稿者スラッグ/」というURL投稿者スラッグは、作成時に指定しなければログインIDをもとに作られる
REST API(/wp-json/wp/v2/users)記事を公開しているユーザーと、それぞれのslugログインしていない人にもslugを返す。slugは投稿者スラッグと同じ値

投稿者スラッグは、URLに使うための名前です。WordPressのユーザー作成の処理では、空ならログインIDをもとに作ります。50字までに切り詰めてURL用に整えるだけなので、英数字のIDならほぼ同じ値になります。何もしなければ、著者ページのURLの末尾がログインIDとほぼ同じになるわけです。

REST APIでも、slugは公開向けの表示項目に入っています。ログインしていない人に返るのは、記事を公開しているユーザーだけです。とはいえ個人ブログなら、記事を書いている本人がそのまま含まれます。多くの場合、それは管理者のアカウントです。筆者のサイトでも、ログインしていない状態でslugが返っていました。

著者ページには、検索エンジンのクローラーも巡回していました。相手がどちらの経路でIDを知ったのかは、ログからは確定できません。ただ、2月の時点ですでに本物のIDで試されていたので、どこかで外から拾われたと考えています。

まもる
推測されにくいIDにしておけば、大丈夫じゃないんですか?
しろ
それが、初期設定のままだと、著者ページのURLにほぼそのまま出ちゃうんだ。ぼくも知らなかったよ

初期設定のまま使っていたCMSが総当たりで突破された例は、IPAの届出事例にもあります。管理画面のURLが推測しやすく、IDとパスワードも簡易だったため、不正にアクセスされ改ざんされたという事例です(2022年上半期、事例204)。

攻撃開始の2分後に、ユーザー一覧を取りに来ていた

10月18日の朝の②では、攻撃が始まった約2分後に、同じIPアドレスが「/wp-json/wp/v2/users」を取得していました。サーバーの応答は200(正常)です。

相手はすでに本物のIDで試していました。それでも途中でIDを確かめる手順が、ツールに組み込まれているのかもしれません。

守りは3層で考える

ここからは、一般的なおすすめ設定です。筆者のサイトの設定をそのまま書いたものではありません。例として、XO SecurityとXserverの項目を挙げます。

3つの層がどこで効くのかを、先に図で見ておきます。

ログインIDが外に出てから入口に届くまでと、3つの層が効く場所 ログインIDは、初期設定のまま投稿者スラッグになり、著者ページのURLとREST APIから外に出る。2層目「IDを出さない」はここでふさぐ。相手はxmlrpc.php(1,600件)とログインページ(1,106件)から試す。1層目「入口を減らす」でxmlrpc.phpは止まるが、ログインページの4割は残る。3層目「漏れても入れない」のユーザー名でのログインの停止、2要素認証、ログイン試行回数の制限で、IDを知られていても止める。 ログインID 初期設定のまま 投稿者スラッグ (ログインIDとほぼ同じ値) 著者ページのURL /author/〇〇/ REST API /wp-json/wp/v2/users 2層目 IDを出さない:ここでふさぐ IDが相手に知られる xmlrpc.php 1,600件 ログインページ 1,106件 1層目 入口を減らす 無効化すれば止まる 止まらない (4割) 3層目 漏れても入れない ユーザー名でのログインを止める(メールアドレスだけ) 2要素認証/ログイン試行回数の制限 IDを知られていても、ここで止める
図1 ログインIDが外に出てから入口に届くまでと、3つの層が効く場所。件数は筆者のサイトのログイン失敗(2026年2月20日〜10月18日、XO Securityのログイン履歴)
層主な対策XO Security/Xserverでの例
1. 入口を減らすXML-RPCを使っていなければ無効化する/国外からの接続を制限するXO Security:XML-RPCの無効化/Xserver:国外アクセス制限
2. IDを出さない投稿者スラッグをIDと別の値にする/使わないなら著者アーカイブを止める/REST APIのusersを未ログインの人に返さないXO Security:投稿者スラッグの編集、投稿者アーカイブの無効化、REST APIの無効化
3. 漏れても入れないユーザー名でのログインを止め、メールアドレスだけにする/2要素認証/試行回数の制限/長くランダムなパスワードXO Security:ユーザー名によるログインの無効化、2要素認証、ログイン試行の制限/Xserver:ログイン試行回数制限
あわせてログインの通知と履歴で、異変に気づけるようにするXO Security:ログインアラート、ログイン履歴

本命は3層目。IDは標準の画面では変えられない

1層目と2層目は、新しく漏れるのを防ぐ対策です。すでに漏れたIDは、相手の手元に残ります。WordPress標準のプロフィール画面では、ログインIDは変えられません。投稿者スラッグを変えても、ログインIDそのものは元のままです。

だから、知られている前提で無力化します。ユーザー名でのログインを止めれば、漏れたIDを入れても通りません。2要素認証を重ねれば、パスワードを当てられても、もう一段の壁が残ります。ログイン用のメールアドレスは、公開している連絡先と分けておくと安心です。

なお、2要素認証はWordPress本体には入っていません。公式ドキュメントでも、プラグインなどで追加するものとされています。

ここが落とし穴

Googlebotを名乗るアクセスで、記事下の読み込みが403になっていた

アクセスログでは、別の問題も見つかりました。ログの時点で、海外のIPから「/wp-json/」へのアクセスが、すべて403(拒否)になっていたのです。

SWELLには、記事下の関連記事やフッターを、ページを開いたあとで読み込む設定があります。その読み込みに使うのがREST APIです。Googlebotを名乗るアクセスによるこの読み込みは、ログの範囲で18回中18回が403でした。AdSenseのクローラーも同じです。日本の読者には、正常に返っていました。

Googleの説明では、Googlebotは既定で米国のIPアドレスからクロールします。米国以外のIPを使うこともあります。Xserverのマニュアルでも、REST APIの国外制限は「/wp-json」が対象とされています。ここに制限がかかると、Googlebotには記事下が空に見える可能性があります。マニュアルに、クローラーへの影響は書かれていませんでした。WordPressの公式ドキュメントも、国単位の恒久的なブロックは避けるよう勧めています。

ただし、ログで分かるのは、その時点のリクエストが403だったことまでです。Googleが最終的に記事下を取得できなかったのかどうかは、ログからは言えません。何が403を返していたのかも、正直なところ特定できていません。同じ日の午後には、海外のサーバーから正常に取得できました。

SWELLで国外制限を使っている方は、Search Consoleの「URL検査」で「公開URLをテスト」を実行してみてください。HTMLに記事下の関連記事が入っていれば、Googleは取得できています。

REST APIを丸ごと止めると、サイトが壊れる

IDを隠すために、REST APIごと止めたくなるかもしれません。これはおすすめしません。ブロックエディターやお問い合わせフォーム、埋め込み表示、前述のSWELLの記事下まで、多くの機能がREST APIで動いています。止めるなら、ユーザー情報(users)のエンドポイントだけに絞るのが無難です。

厄介なのは、未ログインの人だけを止める設定だと、ログイン中の自分には壊れたことが見えない点です。設定を変えたら、シークレットウィンドウで記事下とフォームを確かめましょう。

アプリケーションパスワードは、2要素認証を通らない

スマホアプリなどがREST APIにログインするときは、「アプリケーションパスワード」を使います。ふだんのパスワードの代わりに、APIへ渡す専用のパスワードです。画面で操作するログインではないため、2要素認証の確認は挟まりません。

つまり、2要素認証を設定していても、アプリケーションパスワードが漏れれば、APIからは入れてしまう可能性があります。どの経路で使えるかはプラグインの設定でも変わるので、あわせて確かめてください。使っていないものは「ユーザー」→「プロフィール」の一覧から削除できます。2要素認証を入れるときに、一緒に見直しておくと安心です。

現役SEとして考えたこと:IDは「秘密」ではなく「名札」

正直に書くと、初期設定でIDがURLに出る仕様は知りませんでした。推測されにくいIDにしていたので、それで守れているつもりでした。

セキュリティの勉強では、「識別」と「認証」を分けて考えます。識別は「あなたは誰か」、認証は「本当に本人か」です。IDは識別のための名札で、守る役目はパスワードや2要素認証が担います。基本中の基本ですが、筆者は名札を隠すほうに力を入れていました。

しかも名札の出口は、著者ページとREST APIだけではありません。セキュリティプラグインの中には、コメント欄のクラス名や埋め込み情報からユーザー名を消す項目まで用意されているものがあります。全部ふさぐのは大変です。

隠す努力は続けつつ、本命は「名札を見られても入れない」ほうに置く。今回のログを読んで、筆者はそう考えるようになりました。

筆者は、自治体の情報システム部門に常駐するSEです。職場でもログを扱う場面はあります。ただ、今回のログは種類が違いました。Webサイトへの攻撃のログを、件数・時刻・接続元・端末で並べて読むのは、読み方を調べながらの作業でした。

気づけたきっかけは、ダッシュボードのログイン失敗の件数です。ISMSの管理策A.8.5(セキュリティを保った認証)の実施の手引き(ISO/IEC 27002:2022)に、近い項目があります。ログオンに成功したら、前回以降に失敗した試みの詳細を表示する、というものです。ISMS連載のA.8.1〜A.8.6の回で読んだ一文が、自分のサイトで効いた形でした。

自分のサイトで確かめる

同じことが起きていないか、自分のサイトで確かめられる項目です。

確かめることやり方当てはまったら
著者ページのURLに、ログインIDが出ていないか自分の記事の著者名のリンクを開き、URLの末尾を見る投稿者スラッグを変える。使わないなら著者ページを止める
REST APIが、ユーザー情報を返していないかシークレットウィンドウで「自分のドメイン/wp-json/wp/v2/users」を開くusersのエンドポイントだけ、未ログインの人から保護する
ログイン履歴の成功の行に、心当たりのない接続元がないかセキュリティプラグインのログイン履歴を開くパスワードを変え、ユーザーや記事に見覚えのない変更がないか確かめる
使っていないアプリケーションパスワードがないか「ユーザー」→「プロフィール」の一覧を見る削除する

判断の分かれ道:xmlrpc.phpを止めると、アプリやJetpackはどうなるか

止めるかどうかは、XML-RPCを何に使っているかで決まります。

使い方止めてよいか理由・注意
投稿も管理もブラウザだけ止めてよい困る場面はほぼない。ピンバックも止まる
WordPressの公式アプリを使う止めても、おおむね使える筆者の環境(Android版)では、記事の一覧と編集はREST API経由で動いた。一部の機能は使えなくなった
Jetpackを使っている止めないJetpackはXML-RPCでWordPress.comと通信する
外部の投稿ツールや連携サービスを使う先に確認するXML-RPCで投稿するものは動かなくなる

止められない場合は、試行回数の制限と、長くランダムなパスワードの重みが増します。2要素認証がXML-RPC経由のログインにも効くかは、使っているプラグインの説明で確かめてください。止めた場合でも、前述の通りログインページへの攻撃は残ります。

まもる
結局、何から手をつければいいですか?
しろ
まずは自分の記事の著者名のリンクを開いて、URLの末尾を見てみて。IDが出ていたら、3層目から固めよう

まとめ

  • xmlrpc.phpは、使っていなければ無効化しておきたい入口。ただし止めても、ログインページへの総当たりは残る
  • 筆者のサイトでは8か月で2,706件の失敗があり、約3分の2は本物のログインIDで試されていた
  • ログインIDは、初期設定のままだと著者ページのURLとREST APIから外に出る。標準の画面では変えられないので、漏れる前提でメールアドレスでのログインと2要素認証で無力化するのが本命
  • 侵入されたかは、失敗の件数ではなく成功の行(接続元・時刻・端末)で見分ける
  • 国外アクセス制限は、国内回線からの攻撃を止められない。設定によっては、Googlebotに記事下が見えなくなる可能性もある。REST APIは丸ごと止めない

引っかかった点

  • 何が403を返していたのか。ログの時点では海外からの「/wp-json/」が403で、午後には解消していました。設定の反映の時差なのか、作業中の切り替えなのか、確定できていません
  • 5桁の数字の意味。集中攻撃のたびに、最初に1回だけ5桁の数字を試してきます。何のための値なのかは分かりませんでした
  • 本物のIDがいつ漏れたのか。ログイン履歴は2月20日からしか残っておらず、その時点ですでに試されていました

参考にした一次資料

いずれも2026年10月18日〜19日に確認しました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次