WordPress + Cloudflare 環境でサーバー自身のグローバルIPから大量のアクセスを受けている

概要

数か月前から、WordPressに大量のアクセスを受けてCPUとRAMの使用率が100%近くに張り付いています。
DoSられてるのかと思い、Cloudflareの セキュリティー -> 分析 からアクセス元のIPを見てみたら、なぜか自分のサーバーのグローバルIPから大量にリクエストを受けていました。

一体どういうことなんでしょうか?

アクセス元

パスは全て / に対してアクセスされています。

何が原因?

ユーザーエージェントはWordPress。

変なプラグインが悪さしてるのかと思いましたが、全て無効化しても変わりません。

対処療法

仕方がないので、WordPressのユーザーエージェントに対してJSチャレンジ(BOTはじくやつ)を適応するルールを追加します。

GoogleとかTwitterのBOTまで弾かれたら困るのでそれらは除外し、http.user_agent contains “WordPress” で ユーザーエージェントにWordPressが含まれるリクエストに対してチャレンジさせます。ついでに国外からのリクエストにも。

この対処療法で一応は直りましたが、結局何が原因なんでしょうか😢

原因判明 (追記)

適当にサイト内を巡回すると発生するパターンが判明。記事中で自分自身のサイト(tatuiyo.xyz)のURLを埋め込んでいるページにアクセスしたときのみ発生していました!

こんな感じのやつ。

Query Monitor

WordPressにQuery Monitorプラグインを導入し、何のクエリが暴走しているのか割り出します。

Query Monitorを確認するとHTTP API Callsで時間がかかっているようです。中身を見てみると、

https://tatuiyo.xyz/?p=930	cURL error 28: Operation timed out after 5001 milliseconds with 0 bytes received	

詳細を見ると

WP_oEmbed->discover()
wp-includes/class-wp-oembed.php:460
WP_oEmbed->get_provider()
wp-includes/class-wp-oembed.php:291
hu_embed_html()
wp-content/themes/hueman/functions/init-wp-core-filters.php:30
apply_filters('embed_oembed_html')
wp-includes/plugin.php:205
WP_Embed->shortcode()
wp-includes/class-wp-embed.php:291
WP_Embed->autoembed_callback()
wp-includes/class-wp-embed.php:466
preg_replace_callback()
wp-includes/class-wp-embed.php:466
WP_Embed->autoembed()
wp-includes/class-wp-embed.php:448
apply_filters('the_content')
wp-includes/plugin.php:205
the_content()
wp-includes/post-template.php:256
load_template('wp-content/themes/hueman/tmpl/single-tmpl.php')
wp-includes/template.php:812
locate_template()
wp-includes/template.php:745
get_template_part('tmpl/single-tmpl')
wp-includes/general-template.php:206
hu_get_template_part()
wp-content/themes/hueman/functions/init-front.php:1741
hu_get_content()
wp-content/themes/hueman/functions/init-front.php:27

Huemanテーマの埋め込み機能のバグのよう

ChatGPTに投げる

つまりこういうこと

🧨 問題の根本:自分のURLを自分でoEmbedしようとしていた
🔁 WordPressの標準の流れ:
1.投稿の本文に https://tatuiyo.xyz/?p=930 のような自サイトのURLを貼る

2. WPが oEmbed を試みる

3. テーマの hu_embed_html() が WP_oEmbed->get_provider() を呼び出す

4. WordPress が「よし、そのURL(自分)にアクセスして oEmbed 情報取ろう」として、HTTPリクエスト(curl)で自サイトにアクセス

5. そのアクセスをまた WordPress が受け取り…

6. また oEmbed が発動して…

7. 以下無限ループ!🔥

➡ 結果:Apacheが自分自身に大量アクセスし続けて、CPU100%

Bug Fix

自分宛てにHTTPで埋め込みする情報を取得する代わりに、get_post()で直接投稿データを取るように修正します。
(ChatGPT案)

wp-content/themes/hueman/functions/init-wp-core-filters.php を修正

修正前

if ( !function_exists( 'hu_embed_html' ) ) {
  function hu_embed_html( $html, $url ) {

    if ( !file_exists( ABSPATH . WPINC . '/class-wp-oembed.php' ))
      return $html;
    require_once( ABSPATH . WPINC . '/class-wp-oembed.php' );
    $wp_oembed = _wp_oembed_get_object();
    $provider = $wp_oembed -> get_provider( $url, $args = '' );
    if ( !$provider || false === $data = $wp_oembed->fetch( $provider, $url, $args ) ) {
      return $html;
    }
    // Check that we have a valid $data object
    // for https://wordpress.org/support/topic/error-in-theme-6/
    if ( is_object($data) && isset($data->type) ) {
        $type = $data->type;
        switch( $type ) {
            case 'video' :
              $html = sprintf('<div class="video-container">%1$s</div>', $html );
            break;
        }
    }
    return $html;
  }
}

修正後

if ( !function_exists( 'hu_embed_html' ) ) {
  function hu_embed_html( $html, $url ) {
    $home = parse_url(home_url(), PHP_URL_HOST);
    $url_host = parse_url($url, PHP_URL_HOST);

    // 🔒 自サイトのURLなら、自前で表示内容を組み立て(HTTPアクセスしない)
    if ($home === $url_host) {
        // クエリパラメータから?p=123の形式で投稿ID取得
        parse_str(parse_url($url, PHP_URL_QUERY), $query);
        $target_post_id = isset($query['p']) ? intval($query['p']) : 0;

        if ($target_post_id) {
            $post = get_post($target_post_id);
            if ($post) {
                $title = esc_html(get_the_title($post));
                $permalink = esc_url(get_permalink($post));
                return '<div class="self-embed-container">
                    <strong><a href="' . $permalink . '">' . $title . '</a></strong>
                </div>';
            }
        }

        // 該当投稿が見つからなかった場合はURLリンクとして返す
        return '<a href="' . esc_url($url) . '">' . esc_html($url) . '</a>';
    }

    // 🔄 外部oEmbedは今まで通り処理
    if ( !file_exists( ABSPATH . WPINC . '/class-wp-oembed.php' ))
      return $html;

    require_once( ABSPATH . WPINC . '/class-wp-oembed.php' );
    $wp_oembed = _wp_oembed_get_object();
    $provider = $wp_oembed -> get_provider( $url, $args = '' );
    if ( !$provider || false === $data = $wp_oembed->fetch( $provider, $url, $args ) ) {
      return $html;
    }

    if ( is_object($data) && isset($data->type) ) {
        $type = $data->type;
        switch( $type ) {
            case 'video' :
              $html = sprintf('<div class="video-container">%1$s</div>', $html );
            break;
        }
    }

    return $html;
  }
}

自己ループが解消し、埋め込みも機能しました

もうChatGPTいないと何もできない

おすすめ

コメントを残す

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

Index