2026年5月20日

2026年5月20日

WordPressプラグインの脆弱性をチェックする方法|WP-CLI・WPScan・Wordfenceと定期確認

WordPress サイトへの侵入で多いのは、プラグインやテーマの既知の脆弱性を突く攻撃です。脆弱性の多くは修正版が出ているので、「どのプラグインの、どのバージョンが入っているか」を把握し、脆弱性情報と照らし合わせて更新するのが基本の対策になります。

この記事の WP-CLI のコマンドは WP-CLI 本体のヘルプ、WPScan の API と料金は wpscan.com、Wordfence の機能は wordpress.org のプラグインページで2026年10月時点の内容を確認しています。

確認の流れ

段階 やること 使う道具
1 入っているプラグインとバージョン、更新の有無を一覧にする 管理画面、WP-CLI
2 公開停止になったプラグイン、改ざんされたファイルがないか確かめる WP-CLI
3 既知の脆弱性と照合する WPScan、Wordfence など
4 更新・停止・削除で対処する 管理画面、WP-CLI
5 定期的に繰り返す 自動更新、WP-Cron、通知

手順1: プラグインの一覧と更新の有無を確認する

管理画面では「プラグイン」→「インストール済みプラグイン」で、更新があるプラグインに「新しいバージョンの○○が利用できます」と表示されます。サーバーに SSH で入れるなら、WP-CLI で一覧にすると記録を残しやすくなります。

# 名前・有効/無効・現在のバージョン・更新後のバージョン・自動更新の設定
wp plugin list --fields=name,status,version,update_version,auto_update

# 更新があるプラグインだけ
wp plugin list --update=available

# 無効化されているプラグイン(削除の候補)
wp plugin list --status=inactive

手順2: 公開停止と改ざんを確認する

wordpress.org のプラグインディレクトリでは、未修正のセキュリティ問題などを理由にプラグインが公開停止(closed)になることがあります。公開停止になると更新が配信されないため、管理画面に更新の通知が出ないまま古いバージョンが残ります。

# wordpress.org での状態と最終更新日
wp plugin list --fields=name,version,wporg_status,wporg_last_updated

wporg_status が closed のプラグインは、代わりのプラグインへの移行を検討します。空欄のものは wordpress.org 以外から入れたプラグイン(有料プラグインなど)で、配布元のサイトで更新情報を確認します。

ファイルが改ざんされていないかは、wordpress.org に登録されている元のファイルとの照合で確かめられます。

# すべてのプラグインを照合(wordpress.org で配布されているものだけが対象)
wp plugin verify-checksums --all

# WordPress 本体も照合する
wp core verify-checksums

照合で差分が見つかったファイルは、改ざんの可能性があります。バックアップを取ったうえで、そのプラグインを wordpress.org から入れ直すと元のファイルに戻ります。

手順3: 既知の脆弱性と照合する

WPScan(コマンドラインのスキャナー)

WPScan は、外部からサイトを調べて WordPress 本体・プラグイン・テーマのバージョンを推定し、WPScan の脆弱性データベースと照合するツールです。スキャナー自体は無料ですが、脆弱性の照合には API トークンが必要です。wpscan.com の料金ページ(2026年10月時点)では、無料の Researcher プランは非営利の利用に限られ、API の呼び出しは1日25回までです。業務で使う場合は有料のライセンスが必要です。

# 公式の Docker イメージで実行する例(自分が管理するサイトだけを対象にする)
docker run -it --rm wpscanteam/wpscan \
    --url https://example.com \
    --enumerate vp \
    --api-token YOUR_API_TOKEN
  • --enumerate vp は「脆弱性が登録されているプラグイン」だけを表示します。p にするとプラグインを広く列挙します。
  • --plugins-detection aggressive を付けると検出漏れは減りますが、サイトに大量のリクエストを送ります。他人のサイトに対しては実行しないでください。
  • 結果を JSON で保存するには --format json --output result.json を付けます。

JSON から、脆弱性があるプラグインだけを取り出す例です。

import json

with open("result.json", encoding="utf-8") as f:
    data = json.load(f)

for slug, plugin in (data.get("plugins") or {}).items():
    vulns = plugin.get("vulnerabilities") or []
    if not vulns:
        continue
    version = (plugin.get("version") or {}).get("number", "不明")
    print(f"== {slug} (検出バージョン: {version}) ==")
    for v in vulns:
        print("  -", v.get("title"), "/ 修正バージョン:", v.get("fixed_in") or "未修正")

WPScan の詳しい使い方は WPScanでWordPressの脆弱性を診断する方法 を参照してください。

Wordfence(プラグインのスキャナー)

サーバーにツールを入れられない場合は、Wordfence のスキャンで確認できます。wordpress.org のプラグインページによると、Wordfence のスキャンは既知の脆弱性があるプラグイン・テーマを検出して知らせます。スキャンの設定は Wordfenceのスキャン機能 で説明しています。

手で確かめる

特定のプラグインについて調べたいときは、プラグイン名で WPScan の脆弱性データベース(wpscan.com)や Wordfence Intelligence、Patchstack の公開データベースを検索し、「影響を受けるバージョン」と「修正バージョン」を自分のバージョンと比べます。

手順4: 見つかったときの対処

状況 対処
修正版が出ている バックアップを取ってから更新する。更新後に表側と管理画面の主な操作を確認する
修正版が出ていない 代わりのプラグインに移るか、それまで無効化する。WAF で該当の攻撃を遮断できる場合もある
公開停止(closed) 修正の見込みが立つまで、代わりのプラグインへの移行を検討する
使っていない 無効化だけでなく削除する。無効でもファイルが残っていれば、直接アクセスで攻撃される場合がある
# 更新する前にデータベースのバックアップを取る
wp db export backup-before-update.sql

# 特定のプラグインを更新する
wp plugin update contact-form-7

# 更新があるものをまとめて更新する
wp plugin update --all

更新で表示が崩れたときの戻し方は プラグインの更新で不具合が出たときの対処 を参照してください。

手順5: 定期的に確認する

自動更新を使う

WordPress 5.5 以降は、「プラグイン」→「インストール済みプラグイン」の「自動更新を有効化」で、プラグインごとに自動更新できます。自動更新が行われると、管理者のメールアドレスに結果が送られます。WP-CLI では次のとおりです。

wp plugin auto-updates enable contact-form-7
wp plugin auto-updates status --all

WPScan API で週1回チェックしてメールで知らせる

自動更新を使わないプラグインがある場合は、WP-Cron で WPScan API に問い合わせ、修正版より古いバージョンが入っていたらメールで知らせることができます。API トークンは wp-config.php に定数で書きます(コードやデータベースに直接書かない)。

// wp-config.php(/* That's all, stop editing! */ の行より上)
define( 'WPSCAN_API_TOKEN', 'ここにAPIトークン' );

次のコードは、サイト用の独自プラグインか functions.php に置きます。プラグイン1つにつき API を1回呼ぶので、無料プランの1日25回を超えないよう、対象のプラグイン数に注意してください。

<?php
// 週1回の予定を登録する
function mysite_schedule_vuln_check() {
    if ( ! wp_next_scheduled( 'mysite_weekly_vuln_check' ) ) {
        wp_schedule_event( time(), 'weekly', 'mysite_weekly_vuln_check' );
    }
}
add_action( 'init', 'mysite_schedule_vuln_check' );

function mysite_run_vuln_check() {
    if ( ! defined( 'WPSCAN_API_TOKEN' ) || '' === WPSCAN_API_TOKEN ) {
        return;
    }

    // WP-Cron の中では get_plugins() が読み込まれていないため読み込む
    if ( ! function_exists( 'get_plugins' ) ) {
        require_once ABSPATH . 'wp-admin/includes/plugin.php';
    }

    $found = array();

    foreach ( get_plugins() as $file => $data ) {
        $slug = dirname( $file );
        if ( '.' === $slug ) {
            continue; // フォルダーに入っていない単体ファイルのプラグインは対象外
        }

        $response = wp_remote_get(
            'https://wpscan.com/api/v3/plugins/' . rawurlencode( $slug ),
            array(
                'headers' => array( 'Authorization' => 'Token token=' . WPSCAN_API_TOKEN ),
                'timeout' => 15,
            )
        );

        if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
            continue; // 通信エラー、またはデータベースに無いプラグイン
        }

        $body  = json_decode( wp_remote_retrieve_body( $response ), true );
        $vulns = isset( $body[ $slug ]['vulnerabilities'] ) ? $body[ $slug ]['vulnerabilities'] : array();

        foreach ( $vulns as $vuln ) {
            $fixed_in = isset( $vuln['fixed_in'] ) ? $vuln['fixed_in'] : '';
            if ( '' === (string) $fixed_in || version_compare( $data['Version'], $fixed_in, '<' ) ) {
                $found[] = sprintf(
                    "%s %s\n  %s\n  修正バージョン: %s",
                    $data['Name'],
                    $data['Version'],
                    isset( $vuln['title'] ) ? $vuln['title'] : '(タイトルなし)',
                    '' === (string) $fixed_in ? '未修正' : $fixed_in
                );
            }
        }
    }

    if ( $found ) {
        wp_mail(
            get_option( 'admin_email' ),
            '[' . wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ) . '] 脆弱性が登録されているプラグインがあります',
            implode( "\n\n", $found ) . "\n\nプラグインの管理画面: " . admin_url( 'plugins.php' )
        );
    }
}
add_action( 'mysite_weekly_vuln_check', 'mysite_run_vuln_check' );
  • WP-Cron はサイトへのアクセスがあったときに動くため、アクセスが少ないサイトでは実行が遅れます。サーバーの cron から wp cron event run --due-now を実行すると時間どおりに動きます。
  • 動作確認は wp cron event run mysite_weekly_vuln_check で、その場で実行できます。
  • 有料プラグインなど、WPScan のデータベースにスラッグが無いプラグインは照合されません。
  • コードを外すときは、wp_clear_scheduled_hook( 'mysite_weekly_vuln_check' ) で予定も消します。

使っていないプラグインとテーマを削除する

# 無効化されているプラグインを確認してから削除する
wp plugin list --status=inactive --field=name
wp plugin delete hello-dolly akismet

# 使っていないテーマを確認する
wp theme list --status=inactive
  • wp plugin delete $(wp plugin list --status=inactive --field=name) で無効なプラグインをまとめて消せますが、一時的に止めているだけのプラグインも消えます。一覧を確認してから名前を指定する方が安全です。
  • 削除してもプラグインが作ったデータベースのテーブルやオプションは残ることがあります。必要ならプラグインの説明に従って削除します。
  • テーマは、子テーマの親テーマを消さないよう注意してください。有効なテーマが壊れたときの予備として、デフォルトテーマ(Twenty Twenty-Five など)を1つ残しておくと安心です。

まとめ

  • wp plugin list で更新の有無と wporg_status(公開停止)を確認し、wp plugin verify-checksums --all で改ざんを調べる
  • 既知の脆弱性は WPScan(無料 API は非営利・1日25回)や Wordfence のスキャンで照合する
  • 修正版があれば更新、無ければ代替か無効化。使っていないプラグインは削除する
  • 自動更新、または WP-Cron と WPScan API による週1回の確認で、定期的に見直す

お気軽にご相談ください

お見積りへ お問い合わせへ
▲