WordPress REST APIはApplication Passwords、CORS、レート制限を別々に締める
目次
TL;DR
対象 WordPress REST APIを外部連携(ヘッドレス構成、CI、AI連携)に使っているサイト
要点 Application Passwordsは統合ごとに専用ユーザーで分ける。CORSは認可の代わりにならない。レート制限はPHPの手前に置く
実例 permission_callback がログイン確認だけだったPost SMTPのCVE-2025-24000、Simple JWT Loginの権限昇格CVE-2026-14262
WP Admin Labが7月14日に WordPress REST APIの認証・レート制限・CORS記事 を出していた。
個別の新規CVEではなく、Application Passwords、JWT、レート制限、CORSを2026年の構成で締め直す内容だ。
WordPressを外から操作する経路は、ヘッドレス構成、AI連携、MCP Adapter、Connectors APIと増えている。
この構成で /wp-json/ を「公開してよい読み取りAPI」と「権限を持った操作API」に分けずに公開すると、外部ツールに渡した認証情報の漏洩がそのままサイト操作の漏洩になる。
この流れは ACF 6.8がWordPressをAIエージェントの操作対象にする と WordPress 7.0直前でAI基盤は残りリアルタイム共同編集は見送り でも追った。
Abilities APIとMCP AdapterでWordPressの操作がAIエージェントへ見えるようになり、AI ClientとConnectors APIで外部サービスの接続情報がCore寄りへ移る。
今回はその手前、HTTPの入口とWordPressユーザー権限の設定の話だ。
/wp-json/を閉じる前に管理画面と外部連携を分ける
WordPress公式FAQは、REST API自体を無効化するな、管理画面の機能が壊れる、と書いている。
実際、ブロックエディタやプラグイン管理はREST APIを呼んでいて、API全体を無効化すると管理画面が先に壊れる。
一方で、匿名アクセスを許す必要がないサイトなら、rest_authentication_errors で未ログインのREST APIアクセスを401にする選択肢がある。
API全体を閉じる前に、公開サイト、管理画面、外部クライアント、Webhook、AI連携が使っているルートを分ける。
公開投稿をNext.jsやAstroのフロントから読むだけなら、/wp/v2/posts は残す理由がある。
会員制サイト、社内CMS、投稿管理専用サイトなら、匿名の /wp/v2/users や検索系エンドポイントは残す理由が薄い。
/wp/v2/users は今のWordPressでも、公開投稿を持つユーザーを未認証で列挙できる。
さらに ?search= がメールアドレスにもマッチするため、1文字ずつ試せばメールを絞り込める問題が Trac #53784 として2021年から未修正のまま残っている。
なおWordPress本体はユーザー名を秘密情報として扱わない立場なので、列挙を完全に隠すことよりも、列挙されても突破されない認証側を固める前提で考える。
ACFやMCP Adapterのように外部ツールからWordPressを操作する構成では、リクエストがどのWordPressユーザーとして実行されるかで被害範囲が決まる。
もう1つ、/wp-json/ のREST APIインデックスがある。
プラグインが追加したルート、認証方式、名前空間が並ぶので、攻撃者からはサイト固有の機能一覧に近く見える。
完全に隠せない環境でも、外から見えるルートと認証が必要なルートをログで分けておくと、あとで調査しやすい。
Application Passwordsは便利だがBasic認証の運用になる
WordPress 5.6以降、REST APIの標準的な外部認証としてApplication Passwordsが使える。
公式の管理ハンドブックは、ユーザーに紐づくアプリ別の認証情報で、個別に失効でき、通常のwp-adminログインには使えない、と説明している。
Application Passwordsの出番は、CI、移行スクリプト、外部投稿ツールのように、人がログイン画面を通らない処理だ。
リクエストでは Authorization: Basic ... を送る。
HTTPSは推奨ではなく前提で、非HTTPSのサイトではApplication Passwords自体がデフォルトで無効になる。
Apache CGI環境では Authorization ヘッダーがPHPへ渡らないことがあり、公式FAQにApacheなら SetEnvIf Authorization、nginxなら fastcgi_pass_header Authorization の調整が載っている。
リバースプロキシを挟む構成でも同様にヘッダーが渡らないことがあり、この場合は資格情報が正しくても401が返り続ける。
Application Passwordsを管理者アカウントで使い回すと、漏れた時の被害がそのまま管理者権限になる。
投稿作成だけなら投稿用ユーザーを作り、メディアアップロードだけならそれに必要な権限だけを持たせる。
公式ハンドブックにある wp user application-password list で last_used と last_ip が確認できるので、使っていないものは消す。
JWTはフロントエンドアプリのログイン用だが、WordPress本体の標準機能ではなくプラグイン依存になる。
秘密鍵、トークン期限、リフレッシュトークン、失効リストまで持つなら、WordPressのユーザー権限だけでなくトークン管理の運用も増える。
プラグイン依存ということは、脆弱性の確認もプラグインごとになる。
2026年7月11日公開の CVE-2026-14262 では、Simple JWT Login 3.6.6以前の認証エンドポイントが payload パラメータで管理者のメールアドレスを注入したトークン生成を許し、Subscriberから管理者セッションを取れた。
一方、似た名前のJWT Authentication for WP REST APIには現時点でCVE登録がない。
「JWTプラグイン」と一括りにせず、どれを入れているかで確認先を分ける。
単発の外部スクリプトならApplication Passwords、ブラウザアプリのログインならJWT、管理画面内の操作ならnonceに分ける。
認証方式を増やすほど、失効とログ確認の手順も増える。
CORSは認証の代わりにならない
WordPress REST APIは、受信したOriginヘッダーを検証しない設計になっている。
公式FAQでは、WordPressはCORSではなくnonceでCSRFを防ぐ判断をしており、公開REST APIは任意のサイトからアクセスされうると説明している。
つまり、CORSを狭くしても、curlでAPIを直接叩くリクエストはそのまま通る。
CORSはブラウザが別オリジンのJavaScriptを止める仕組みで、APIそのものの認可判定ではない。
ログイン済みブラウザからCookie付きで書き込みを行う設計なら、Access-Control-Allow-Credentials: true と許可オリジンの扱いが狙われる箇所になる。
Access-Control-Allow-Origin: * を返しながら、認証済み操作も同じ入口で受ける構成は避ける。
複数の正規フロントエンドがあるなら、受信したオリジンを照合して、許可リストにある時だけそのオリジンを返す。
実装は公式FAQにも載っていて、rest_send_cors_headers のデフォルト付与を外し、rest_pre_serve_request フックで自前のヘッダーを返す形になる。
その場合でも、書き込み系はnonce、Application Passwords、JWT、権限チェックのいずれかで必ず止める。
同じ誤解は、ローカルツールでも問題になっていた。
OpenCodeの未認証RCE記事 では、無制限CORSのHTTPサーバーの前で「ローカルで動かしているから安全」という前提が崩れていた。
WordPressは公開サーバーなので条件は違うが、CORSに認可を代行させられない点は共通する。
レート制限はPHPの手前で止める
WordPress本体にはREST APIのレート制限が組み込まれていないので、どこかで足すしかない。
REST APIへの総当たり、ユーザー列挙、検索エンドポイントの連打は、WordPressのPHPまで到達した時点で、止めるだけでもWordPress本体のブート処理が走ってしまう。
nginxの limit_req、Apacheの mod_evasive、CloudflareやSucuriのWAF、ホスティング側のレート制限で、PHPの手前に1段置く。
数値の目安として、冒頭のWP Admin Lab記事は認証系エンドポイントに5リクエスト/分、公開読み取りに60リクエスト/分という多層構成の例を出している。
ただし、企業ネットワーク、モバイル回線、Cloudflareの背後、学校や店舗のWi-Fiでは、多数の正規ユーザーが同じ出口IPに見える。
IP単位の制限は、こういう場所の正規ユーザーをまとめて絞ってしまう。
JWTのトークン発行エンドポイントやApplication Passwordsを使う書き込み系は厳しめにし、公開投稿の読み取りはキャッシュやCDNへ逃がす。
HTTP 429を返すなら、Retry-After も返す。
正規クライアントが再試行間隔を読めるし、ログ上でも「落ちた」のではなく「意図して絞った」と分かる。
API利用者が自分のスクリプトなら、429時に指数バックオフするだけでサーバー側の負荷がかなり変わる。
AI連携では専用ユーザーを作る
WordPress 7.0のAI ClientやConnectors API、ACF 6.8のAbilities API統合は、WordPressを外部ツールから操作しやすくする方向の機能だ。
Connectors APIは保存したAPIキーを設定画面とREST APIレスポンスの両方でマスクするなど、Core側も接続情報の露出には手を打っている。
それでも外部ツールが増えるほど、REST API、Application Passwords、OAuth、MCP Adapter、プラグイン固有エンドポイントの区別がつきにくくなる。
外部AIツールに使わせるユーザーは、普段の管理者アカウントとは分ける。
投稿下書きの作成、メディアアップロード、カスタム投稿タイプの操作、設定変更、プラグイン管理は別物だ。
とくに manage_options や install_plugins を持つユーザーで外部連携を動かすと、APIトークンの漏洩がそのままサイト管理の漏洩になる。
公式の MCP Adapter紹介記事 も、本番のMCPアクセスには権限を絞った専用ユーザーを作り、公開エンドポイントに載せるabilityは読み取り系を優先するよう書いている。
セルフホストのMCP Adapterの認証は現状Application PasswordsかJWTで、OAuth 2.1が使えるのはWordPress.com側だけだ。
つまりAI連携で使う認証情報も、Application Passwordsとして発行・失効を管理することになる。
Vertex Addons for Elementorの Subscriber権限から任意プラグインを有効化できたCVE は、REST APIではなく admin-ajax.php 経由の脆弱性だった。
あちらは current_user_can('install_plugins') のチェック結果を確認せずに処理を続けてしまう実装ミスで、認可チェックが書いてあっても効いていなければそのまま処理が進む、という例になっている。
REST APIでも同じで、permission_callback が is_user_logged_in() だけなら、Subscriberでも通る操作になっていないか確認する。
実例もある。40万サイトが入れているPost SMTPの CVE-2025-24000 は、メールログ取得の permission_callback がログイン確認だけだったため、Subscriberで登録すると管理者宛のパスワードリセットメールを含むメールログを取得できた。
公式ドキュメントも、ログイン確認ではなく current_user_can で操作単位の権限を確認しろと書いている。
REST APIを残すサイトの境界
公開読み取りAPIとしてWordPressを使うサイトなら、匿名GETを完全には閉じない。
その代わり、ユーザー一覧、検索、独自プラグインのルート、書き込み系の permission_callback を見直す。
ブラウザから使うフロントエンドが決まっているならCORSの許可オリジンを絞り、Cookie認証を使う時だけ Access-Control-Allow-Credentials を許す。
外部ツールから投稿やメディアを操作するサイトなら、Application Passwordsを統合ごとに分ける。
管理者本人の認証情報を流用せず、専用ユーザー、HTTPS、Authorizationヘッダーの通過確認、失効手順までセットで置く。
WordPressをAIエージェントや自動化ツールから操作するサイトでは、/wp-json/ のルート、Application Passwords、JWTプラグイン、MCP Adapter、AI系プラグインが別々の場所で増えていく。
どれも入口が違うだけで、最後に実行されるのはWordPressユーザーのcapabilityなので、管理する一覧は1つにまとまる。
参考:
- Sécuriser une API REST WordPress : authentification, rate limiting et CORS en 2026
- WordPress REST API Handbook: Authentication
- WordPress Advanced Administration Handbook: Application Passwords
- WordPress REST API Handbook: Frequently Asked Questions
- From Abilities to AI Agents: Introducing the WordPress MCP Adapter