タグアーカイブ admin-ajax.php

WP Umbrellaが管理画面で403になる原因と解決法

WP Umbrellaが管理画面へ送る内部リクエストが一部のレンタルサーバーで403エラーになるのは、Refererヘッダーが欠けているためだ。mu-pluginでヘッダーを付与すると一時的に解決でき、修正版への更新で恒久的に直る。

WP Umbrellaの403エラーはリクエストヘッダー不足が原因

WP Umbrellaの403エラーはリクエストヘッダー不足が原因

WP Umbrellaは監視・管理のため、WordPressのadmin-ajax.phpへ内部リクエストを送っている。このバックグラウンド通信にはUser-Agentとして「WPUmbrella」が使われ、CookieもRefererも含まれない。多くのホスティングはこのリクエストを許可するが、セキュリティ規則が厳しい環境では「403 Forbidden(アクセス拒否)」を返す。

ホスト側のWAFやmod_securityは、CookieやRefererのないPOSTリクエストを不正アクセスと判断することがある。特に/wp-admin/配下への直接POSTは警戒されやすい。結果としてWP Umbrellaのダッシュボードにプラグインの更新情報が一部表示されない、サイトの監視データが欠けるといった影響が出る。

Before(エラー状態)
WP Umbrella → POST admin-ajax.php → 403禁止

ヘッダーにRefererなし、Cookieなし。WAFがブロックする。

↓
After(修正後)
WP Umbrella → POST admin-ajax.php → 200成功

Refererヘッダーを追加するとサーバーが受け付ける。

■ エラー状態 ■ 修正後

上のデモは、WP Umbrellaの内部リクエストがRefererの有無でどう変わるかを示している。403はサイトの表示を壊さないが、管理画面側で更新データの取得漏れが起きる。

mu-pluginでRefererヘッダーを追加する手順

mu-pluginでRefererヘッダーを追加する手順

プラグインの修正版が配布されるまでの間は、mu-plugin(マスタードロップイン)でWP UmbrellaのリクエストにRefererヘッダーを自動付与するのが確実だ。mu-pluginは/wp-content/mu-plugins/に置くPHPファイルで、管理画面から無効化できない代わりに毎回確実に読み込まれる。

STEP 1 FTPまたはファイルマネージャーで/wp-content/mu-plugins/を開く
↓
STEP 2 mu-pluginsフォルダが無ければ新規作成する
↓
STEP 3 下のコードを保存したPHPファイルをアップロードする
↓
STEP 4 WP Umbrellaのダッシュボードが正常に更新されるか確認する

ファイル名は任意でよいが、add-referer-for-wp-umbrella.phpのように役割が分かる名前が管理しやすい。

<?php
/**
 * WP Umbrella の内部リクエストに Referer ヘッダーを付与する
 */
add_filter( 'http_request_args', function ( $args, $url ) {
    if ( 'WPUmbrella' === ( $args['user-agent'] ?? '' )
        && 0 === strpos( $url, admin_url() )
        && empty( $args['headers']['Referer'] ) ) {
        $args['headers'] = (array) ( $args['headers'] ?? array() );
        $args['headers']['Referer'] = admin_url();
    }
    return $args;
}, 10, 2 );

このコードが何をしているのか

http_request_argsフィルタは、WordPressがHTTPリクエストを送る直前に引数を書き換えられるフックだ。ここではUser-Agentが「WPUmbrella」であること、リクエスト先が管理画面のURLであること、Refererが未設定であることの3条件が揃った場合だけ、管理画面URLをRefererとして追加している。

条件を限定しているため、WP Umbrella以外の通信には影響しない。WP Umbrella側のリクエスト検証にも変更はない。修正版に更新した後はこのmu-pluginを削除して問題ない。

プラグインのアップデートで恒久的に解決する

プラグインのアップデートで恒久的に解決する

この問題はWP Umbrellaの開発チームが把握しており、次期リリースで内部リクエストのUser-AgentとRefererが修正されることが発表されている。具体的には、User-Agentが「WordPress/」で始まる文字列になり、Refererに管理画面URLが付与される。

WordPress管理画面の「プラグイン」→「インストール済みプラグイン」でWP Umbrellaの更新通知が表示されたら、通常どおり更新する。更新後は403になる頻度がなくなり、mu-pluginの応急処置は不要になる。更新が配布されていない間は、mu-pluginを置いたまま運用してかまわない。

同じ403が別のプラグインでも出た場合の切り分け

同じ403が別のプラグインでも出た場合の切り分け

Refererヘッダー不足による403は、WP Umbrella以外の監視・バックアップ・API連携系プラグインでも起こり得る。原因を切り分けるには、まず該当プラグインが管理画面のadmin-ajax.phpへPOSTを送っているかをエラーログやプラグインの仕様で確認する。

特定のプラグインだけが403になる場合は、ホスティングのWAF設定を緩める前に、mu-pluginで当該プラグインのリクエストだけにRefererを追加できないか検討する。WAF全体を無効化するとサイトの防御力が下がるため、最終手段として扱う。

原因がプラグインの実装にある場合は、開発元にフォーラムやサポートから報告すると、今回のようにプラグイン本体で修正される可能性が高い。

よくある質問

403エラーが出てもサイト表示には影響あるか

このケースではサイトの表示や一般ユーザーの操作には影響しない。WP Umbrellaのバックグラウンド通信だけが拒否されるため、ダッシュボード上の更新情報取得や監視データの一部が欠ける形になる。

mu-pluginフォルダが見つからない場合はどうするか

/wp-content/の直下にmu-pluginsフォルダがない場合は、自分で新規作成すればよい。フォルダ名は必ずmu-pluginsにする。その中にPHPファイルを直接置くとWordPressが自動で読み込む。

このmu-pluginはWP Umbrella以外に影響を与えるか

影響しない。コード内でUser-Agentとリクエスト先を限定しているため、WP Umbrellaの管理画面への内部通信だけにRefererを追加する。他のプラグインのHTTP通信には触れない。

プラグイン更新後にmu-pluginは削除すべきか

修正版WP Umbrellaに更新したらmu-pluginは不要になるので、削除してよい。残していても通常は害がないが、意図しない通信パラメータの重複を避けるため削除するのが綺麗だ。

403の原因がWP Umbrella以外だった場合の確認ポイントは何か

全プラグインを無効化して1つずつ再有効化し、どのプラグインで403が再発するかを確認する。テーマを標準テーマに切り替える方法も有効だ。サーバーのWAFログやPHPエラーログも手がかりになる。

この記事のポイント

  • WP Umbrellaの内部リクエストがReferer不足で403になる
  • mu-pluginでRefererを付与すると応急処置できる
  • 開発元が修正した次期リリースで恒久解決する
  • サイト表示への影響はなく、管理データの取得漏れが起きる
  • 同様の403は他プラグインでもWAF設定の見直しで対処する
admin-ajax.phpへのボット大量アクセスでCPUが100%になる時の遮断と対策

admin-ajax.phpへのボット大量アクセスでCPUが100%になる時の遮断と対策

admin-ajax.php へのボットの大量リクエストでサーバーの CPU 使用率が 100% に張り付く場合は、一括遮断ではなく攻撃対象の AJAX アクションをログから特定し、不要な処理を止めた上で正規アクセスを通すレート制限を導入するのが現実的な対策だ。

なぜボットは admin-ajax.php を集中的に叩くのか

admin-ajax.php は WordPress が全 AJAX リクエストを受け付ける入口にあたるファイルだ。フォーム送信、商品カートの更新、ハートビート API による自動保存など、フロントエンドと管理画面の両方で使われている。

ボットがここを狙う理由は、ログイン不要でアクセスできる公開エンドポイントでありながら、リクエストごとに WordPress 全体を読み込んで PHP を実行するため、少ないリクエスト数でもサーバー負荷を大きくできる点にある。

URL に action パラメータを付けて呼び出す構造になっており、たとえば問い合わせフォームの送信処理、EC サイトのカート更新、管理画面の自動保存など、あらゆる AJAX 処理がここを通る。ボットはこれを悪用し、処理の重い action を何度も叩いてサーバーを圧迫する。

攻撃対象の AJAX アクションをログから特定する

攻撃対象の AJAX アクションをログから特定する

対策の最初の一歩は、どの action が攻撃されているのかをアクセスログで確認することだ。闇雲に遮断ルールを増やしても、正規のフォームを巻き込むだけで根本解決にはならない。

アクセスログで action パラメータを調べる

Apache なら /var/log/apache2/access.log、nginx なら /var/log/nginx/access.log にリクエスト履歴が残っている。admin-ajax.php を含む行を抽出し、action= の後ろに続く値を集計すると、攻撃対象が見えてくる。

サーバー管理画面からログをダウンロードできる場合も多い。Cloudflare を利用しているなら、分析画面のセキュリティイベントから admin-ajax.php 宛てのリクエストを絞り込み、クエリ文字列を確認する手もある。

STEP 1 アクセスログで admin-ajax.php へのリクエストを抽出する
↓
STEP 2 action パラメータの値を集計して攻撃対象を特定する
↓
STEP 3 不要なアクションを WordPress 側で無効化する
↓
STEP 4 正規アクセスを通すレート制限を導入する

攻撃対象を特定せずに全リクエストを遮断すると、問い合わせフォームが動かなくなる原因になる。必ずログ調査から始めるのが安全だ。

代表的な攻撃対象を把握しておく

攻撃対象になりやすいのは、外部から呼び出せる未ログインユーザー向けの AJAX アクションだ。問い合わせフォームの送信処理、ハートビート API の更新処理、EC サイトのカート更新などが代表例になる。自分のサイトに不要なプラグインが残した action がしばしば悪用される。

正規のフォームを壊さずにレート制限する

正規のフォームを壊さずにレート制限する

すべての admin-ajax.php リクエストにチャレンジ(CAPTCHA や JS チャレンジ)を適用すると、ブラウザ内の JavaScript が裏で送る AJAX リクエストまで巻き込まれ、フォームが正常に動作しなくなる。これが一括チャレンジ方式が失敗する理由だ。

有効なのは、攻撃対象の action だけを条件にしたレート制限だ。単位時間あたりのリクエスト数を IP アドレスごとに制限し、上限を超えたら一時的にブロックする。正規ユーザーのフォーム送信は頻度が低いため、まず引っかからない。

Before 全リクエストにチャレンジを適用
ブラウザ内の AJAX リクエストがチャレンジに引っかかり、問い合わせフォームが送信できなくなる
正規ユーザーまでブロックしてしまう
↓
After 攻撃対象の action だけをレート制限
頻度の低い正規のフォーム送信は通過し、ボットだけが制限を超えてブロックされる
正規ユーザーへの影響を最小限に抑える
■ 一括遮断の失敗例 ■ レート制限による解決

レート制限の閾値は、通常のフォーム送信頻度を大きく上回る値に設定する。たとえば同一 IP から 1 分間に 30 回を超える admin-ajax.php へのリクエストを制限する場合、正規ユーザーが 30 回もフォームを送ることはない。

WordPress側で不要な AJAX アクションを無効化する

WordPress側で不要な AJAX アクションを無効化する

攻撃対象のアクションが自分のサイトで使われていないなら、WordPress のフックを使ってそのアクション自体を止めてしまうのが最も確実だ。リクエストが届いても処理が実行されず、サーバー負荷は大きく下がる。

未ログインユーザー向けのアクションを止める

wp_ajax_nopriv_ から始まるフックは未ログインの訪問者からの AJAX 処理を担当する。使っていない action を特定したら、テーマの functions.php に下のコードを追加して 403 を返すようにする。

add_action('admin_init', function() {
    if (defined('DOING_AJAX') && DOING_AJAX) {
        $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
        if (in_array($action, array('unused_action_1', 'unused_action_2'), true)) {
            wp_die('403 Forbidden', 'Forbidden', array('response' => 403));
        }
    }
}, 1);

action 名はログで特定した値に置き換える。これで、その action へのリクエストは WordPress の初期化直後に拒否され、プラグインのコードが呼ばれる前に処理が終わる。

ハートビート API の負荷を下げる

管理画面や投稿編集画面で自動保存・ロック通知のために heartbeat が一定間隔で動く。編集画面を開きっぱなしにするボットがいると負荷がかさむ。管理画面を普段使わないサイトなら、heartbeat の頻度を下げるか停止する手もある。

add_filter('heartbeat_settings', function($settings) {
    $settings['interval'] = 300;
    return $settings;
});

interval の単位は秒だ。300 にすると編集画面の自動保存間隔が 5 分になり、サーバーへの負荷が大きく減る。完全に止めるのは編集画面のロック機能に影響するため、まず間隔を延ばすところから始めるとよい。

サーバー設定でボットを直接遮断する

サーバー設定でボットを直接遮断する

Cloudflare やサーバーレベルの設定でも、条件付きの遮断やレート制限を入れられる。正規のフォームを止めずにボットだけを弾くには、リファラー情報やリクエスト頻度を条件に使うのがポイントだ。

リファラーを条件に弾く

正規の AJAX リクエストは、自分のサイトのページから送られるため Referer ヘッダーに自ドメインが入る。一方、ボットは Referer なしや偽装した値で送ることが多い。Apache なら .htaccess で Referer を条件に遮断できる。

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} /wp-admin/admin-ajax.php
RewriteCond %{HTTP_REFERER} !^https?://example.com/ [NC]
RewriteRule ^ - [F,L]
</IfModule>

これは自ドメイン以外からの Referer を持つ admin-ajax.php リクエストを 403 で拒否する設定だ。example.com の部分は自分のドメインに置き換える。ただし一部のブラウザ設定で Referer が送られないケースもあるため、完全な遮断には向かない。あくまで補助的な対策として使う。

nginx でレート制限する

nginx を使っているなら limit_req ディレクティブで admin-ajax.php 専用のレート制限を定義できる。サーバーブロックに以下の設定を追加する。

limit_req_zone $binary_remote_addr zone=ajax:10m rate=1r/s;

location = /wp-admin/admin-ajax.php {
    limit_req zone=ajax burst=20 nodelay;
    limit_req_status 429;
    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    include fastcgi_params;
}

この設定では、同一 IP から 1 秒あたり 1 リクエストを超えると、バースト(突発的な超過)が 20 回まで許容される。超過分は 429(Too Many Requests)で拒否される。正規ユーザーのフォーム送信はこの制限内に収まるが、ボットの連打はすぐに上限を超える。

よくある質問

すべての admin-ajax.php リクエストを遮断してもよいか

やめておいたほうがよい。問い合わせフォーム、カート更新、検索機能など、多くのサイトで admin-ajax.php が使われている。全遮断すると重要な機能が動作しなくなる。

Cloudflare の Bot Fight Mode だけでは不十分なのか

Bot Fight Mode は既知のボットを自動判定して弾く基本的な防御で、未知のボットや単純なスクリプトには効果が薄い。特定のエンドポイントを狙う攻撃には、レート制限やアクション単位の対策を組み合わせる必要がある。

アクセスログの場所がわからない場合はどうするか

レンタルサーバーの管理画面にアクセスログの閲覧機能があることが多い。わからなければサーバー会社のサポートに問い合わせるか、Cloudflare の分析画面でリクエスト履歴を確認するとよい。

ボット対策プラグインだけでも解決できるか

Wordfence や Limit Login Attempts Reloaded のようなセキュリティプラグインは総合的な保護を提供するが、admin-ajax.php への大量アクセスには専用のレート制限やサーバー設定のほうが効果的な場合が多い。プラグインとサーバー設定の併用が望ましい。

この記事のポイント

  • admin-ajax.php は公開エンドポイントで、ボットが少ないリクエスト数でも負荷をかける
  • まずアクセスログで action パラメータを集計し、攻撃対象を特定する
  • 一括チャレンジは正規フォームを壊すため、action 単位のレート制限を使う
  • 使っていない AJAX アクションは functions.php から 403 で拒否する
  • サーバー設定(.htaccess や nginx)でも Referer 条件や limit_req で守れる