WordPress XML-RPC:無効化する方法3つ&xmlrpc.php攻撃の見分け方|Jetpackの例外

WordPressサイトのアクセスログに、同じIPからxmlrpc.phpへのリクエストが数百件続いているなら、WordPress XML-RPCを狙ったパスワード総当たりです。WordPressサイトを複数運営しているNginx + Apache構成サーバーの1日分のアクセスログ8,624件を分類し、無効化する方法3つを実際に適用してレスポンスコードを確認した結果、どの層でブロックすればPHPの実行まで減るのかを整理しました。
XML-RPCは、HTTPでXML文書をやり取りしてWordPressの機能をリモートで呼び出す規約で、WordPress 3.5からは設定画面のオン・オフ項目がなくなり、インストール直後からオンの状態で動いています。
1️⃣ xmlrpc.phpの役割と使われる場面
REST APIができてからは[xmlrpc.php]をもう使われないファイルと思いがちですが、Jetpackは今もこのファイルを通してWordPress.comとサイトをつないでいます。Jetpackの公式ドキュメントはXML-RPC接続に依存すると明記しており、XML-RPCを標準でブロックするホスティングではJetpackの接続自体が失敗します。今でも事務所にFAX番号を1つ残しておくのと同じです。
WordPress 7.1時点でこのファイルが受け付けるメソッドは77個で、記事・メディア・コメントを扱うwp系50個、古いブログ規約(MetaWeblog、MovableType、Blogger)23個、ピンバック2個、テスト用のdemo 2個で構成されています。
wp系はリクエスト本文に含まれるユーザー名とパスワードでログインするため、ログイン画面を通りません。記事の作成と編集、メディアのアップロード、コメント管理はすべてこの系統で、古いブログ規約23個は、他のブログツールと同じ方式で記事をやり取りするために残されているメソッドです。
demo.sayHelloは、WordPress XML-RPCに入っているテスト呼び出しです。送ると[Hello!]の一言だけを返し、ログインが不要なので、XML-RPCが動いているかを確認する用途で使います。対になるものとして、2つの数を足して返すdemo.addTwoNumbersがあります。
いずれもPOSTでXML本文を送らないと応答せず、ブラウザーでアドレスを開くと[XML-RPC server accepts POST requests only.]という文言とともに405が返ります。
ピンバックもこのファイルの担当です。記事を公開すると、本文でリンクしたサイトのxmlrpc.phpにピンバックを送り、受け取った側のサーバーは送信元のアドレスを直接開いてリンクが本当にあるかを確認します。
2️⃣ アクセスログで見るxmlrpc.php攻撃
パスワード総当たりはログイン画面を通らず、[xmlrpc.php]から入ってきます。WordPressサイトを複数運営しているNginx + Apache構成サーバーの10月14日0時から20時までのアクセスログでは、POST /xmlrpc.phpリクエストは8,624件、IPは72個で、そのうち19個のIPがちょうど300件ずつ送っていました。
あるIPは14時9分から37分までの28分間に300件を送り、5〜6秒の間隔が最後まで一定でした。8,524件のリクエストはUser-AgentにJetpackまたはWordPress.comを名乗って入ってきており、[Jetpack by WordPress.com (Jetpack 13.0; WordPress 6.3)]のように実際のJetpackの形式をまねた値を、同じIPが複数使い分けていました。
同じ日の14時までは、セキュリティプラグインのXML-RPCオフがこのリクエストをホームへ302で戻しており、14時以降にNginxでブロックしてからは403で終わりました。302は本文が0バイトでもリクエストごとにPHPが実行された結果で、403はPHPを通らなかった応答です。
3️⃣ system.multicallとパスワード総当たり
[system.multicall]で1回のリクエストに数百個のパスワードを試せるという説明が多いですが、WordPressは1つのリクエスト内でログインが一度失敗すると、残りの呼び出しのパスワードを照合しません。最初の呼び出しだけがwp_authenticateを通り、以降の呼び出しは検査なしで同じエラー[Incorrect username or password.]を返します。
呼び出しの数だけ応答はそのまま返るため、間違ったパスワード100個を入れたリクエスト1つに対し、23KBの応答でエラー100個が0.12秒で返ります。同じリクエスト内で正しいパスワードを後ろに置くと前の呼び出しの失敗のために一緒に拒否され、前に置くとその呼び出しだけ成功します。パスワードの照合は1回ですが、XMLの解析とWordPressの読み込みはリクエストごとに繰り返されるため、この攻撃のサーバー負荷はログイン検査ではなくリクエスト数そのものから生じます。
4️⃣ XML-RPCを無効化する方法3つとレスポンスコード
プラグインでXML-RPCをオフにしても、[xmlrpc.php]へのリクエストはPHPまで届きます。フィルターでオフにした状態で呼び出し100個のリクエストを送っても、HTTP 200でエラー100個(24KB)がそのまま返ってきました。どの層で拒否するかによって方法は3つに分かれ、レスポンスコードもそれぞれ異なります。
1. コードでxmlrpc_enabledフィルターをオフにする
テーマのfunctions.phpに、[xmlrpc_enabled]フィルターをfalseにするadd_filterを1行入れれば済みます。この状態では、ログインが必要な呼び出しは[XML-RPC services are disabled on this site.]というエラーとHTTP 405で拒否されますが、ログインの要らないテスト呼び出しdemo.sayHelloは引き続き200で応答します。
2. プラグイン設定でオフにする:Perfmatters・Hide My WP
PerfmattersやHide My WPなどのプラグインで、Disable XML-RPC設定を使って無効化する方法です。Hide My WPは上と同じフィルター方式なので応答も同じですが、Perfmattersは同じフィルターに加えて[xmlrpc.php]への直接リクエストを403で終わらせ、ピンバックのヘッダーとピンバックリンクまで取り除くため、オフにするとGETもPOSTも403で応答します。ただし403を返すためにWordPressがinit段階まで実行されるので、応答まで0.13〜0.15秒と開いているときと同じ時間がかかり、PHPの負荷は残ります。
3. Webサーバーでブロック:Nginx・Apache
NginxでもApacheでもPHPの手前でリクエストを拒否するため、プラグイン方式と違ってWordPressは実行されません。
# Nginx — serverブロック内、PHP処理のlocationより上に
location = /xmlrpc.php {
deny all;
}このlocationブロックはPHP処理のlocationより上に置き、[xmlrpc.php]に完全一致するリクエストをNginxがその場で拒否します。入れた後は設定を点検して再読み込みしないと反映されません。
nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successfulsystemctl reload nginx再読み込み後はGETもPOSTもPHPまで行かずに403で終わり、応答本文はNginx標準の403ページ153バイトで、応答まで0.02秒です。
# Apache — サイトルートの.htaccess(Apache 2.4)
<Files xmlrpc.php>
Require all denied
</Files>Apacheだけを使うサーバーでは、サイトルートの.htaccessに上のFilesディレクティブを入れればすぐに適用され、Apache標準の403ページ(199バイト)が同じ0.02秒で返ります。
curl -s -o /dev/null -w '%{http_code}' -X POST https://example.com/xmlrpc.php -d '<methodCall><methodName>demo.sayHello</methodName></methodCall>'403上のコマンドはテスト呼び出しdemo.sayHelloをPOSTで送ってレスポンスコードを確認するもので、Webサーバーでブロックした後は403が返ります。[-o /dev/null]と[-w]のオプションを外して送ると本文が見え、[Hello!]が返れば開いている状態ですが、フィルターだけでオフにしたサイトも同じ応答なので、ログイン呼び出しまで送らないと405は確認できません。
5️⃣ オフにしてはいけない場合と代替策
Webサーバーで丸ごとブロックする方法は効果が最も大きいですが、JetpackでWordPress.comと接続しているサイトには向きません。JetpackはXML-RPC接続に依存しており、WP EngineのようにXML-RPCを標準でブロックするホスティングでは、Jetpack側の案内に従って例外を開けないと接続できません。ピンバックを受け取りたいサイトも同様です。
こうしたサイトでは、Jetpackが公開しているIPリスト(jetpack.com/ips-v4.txt)だけを許可して残りを拒否する形に絞ります。Nginxのlocationブロック内にIP帯の数だけallow行を入れ、最後にdeny allを置けば済みます。
完全にブロックできないなら、ログインしていないリクエストに1分あたりの回数制限をかける方法が残ります。設定は wp-adminのセキュリティ の記事のNginx速度制限の項目と同じで、ログインCookieのないリクエストだけを数えるため、ログインした管理者の作業には影響しません。
🔢 FAQ & おすすめコンテンツ










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