WordPressがマルウェアに感染した際、「スキャンして怪しいファイルを削除」という対応では再感染のリスクが残ります

本記事では実際に対応したWordPress感染事例をもとに、完全クリーンな状態へ復旧するための具体的な手順を解説します。スキャン&削除で終わらせない、安全な復旧フローです。


    今回の感染事例の概要

    対象サイト: 保育園のWordPressサイト
    発覚のきっかけ: Google検索からアクセスすると、無関係なショッピングサイトに飛ばされる
    特徴: 直接URLを入力してアクセスすると正常に表示される(管理者に気づかれにくい)
    感染範囲: WordPress設定ファイル、.htaccess、テーマ、プラグイン全域
    推定原因: 長期間のプラグイン・WordPressコアの更新放置による脆弱性を悪用した自動攻撃

    今回の感染は非常に高度な3段階の攻撃手口が組み合わされていました。

    手口①:リファラーベースの条件付きリダイレクト

    アクセス元(リファラー)を判定し、Google・Yahoo・Bingなどの検索エンジン経由の場合のみ不正なサイトへリダイレクトさせる手口です。

    直接URLを入力した場合・管理者がログインした状態ではリダイレクトが発生しないため、サイト管理者が感染に気づきにくい構造になっています。

    // 簡略化した例(実際はさらに難読化されている)
    $ref = $_SERVER['HTTP_REFERER'];
    if (preg_match('/google|yahoo|bing/i', $ref)) {
        header('Location: https://悪意のあるサイト/');
        exit;
    }
    

    手口②:バックアップによる自己再生機能

    削除しても自動的に復活する仕組みが仕込まれていました。マルウェアのコピーが .min.css.min.js.png.gif などの一見無害に見えるファイル名で複数箇所に隠されており、一部を削除しても残ったファイルが復元します。

    これが、スキャン&削除での対応が危険な最大の理由です。

    手口③:三重の難読化によるスキャン回避

    コードを人間にも自動スキャンツールにも読めないよう難読化していました。

    • 関数名の分割: evalev + al のように文字列連結で書く
    • 16進数・8進数エンコード: 文字を x65x76x61x6c のような形式に変換
    • goto文による制御フロー難読化: 処理の流れを追いにくくする

    STEP 1:感染状況の調査とバックアップ

    まず外部ツールで感染を確認する

    サイトにアクセスする前に、外部ツールで状態を確認します。

    ツール 用途
    Google Safe Browsing Googleによるマルウェア登録確認
    VirusTotal URLをマルチエンジンでスキャン
    Sucuri SiteCheck マルウェア・ブラックリスト確認

    感染状態のままバックアップを取る

    クリーンアップ前に感染状態のファイルをバックアップします。これは後の原因解析と証拠保全のために重要です。

    重要: このバックアップは「感染した状態の記録」で、安全な場所に保管するだけです。復旧には画像ファイル以外使いません。

    サーバーアクセスログを確認する

    不正アクセスの経路・時期を特定します。

    # アクセスログから不審なPOSTリクエストを確認
    grep "POST" /var/log/apache2/access.log | grep -v "wp-cron|xmlrpc"
    
    # 最近変更されたPHPファイルを確認
    find /path/to/wordpress -name "*.php" -newer /path/to/wordpress/wp-config.php -ls
    

    STEP 2:マルウェアの解析

    削除前に、どこに・何が・どのように仕込まれているかを把握します。

    不審なコードのパターン検索

    # base64エンコードされたコードを探す
    grep -rn "base64_decode" /path/to/wordpress --include="*.php"
    
    # eval と base64 の組み合わせ(典型的なマルウェアパターン)
    grep -rn "eval.*base64_decode|base64_decode.*eval" /path/to/wordpress --include="*.php"
    
    # 難読化された文字列(hex・octal)を探す
    grep -rn "\\x[0-9a-fA-F]{2}" /path/to/wordpress --include="*.php"
    
    # リダイレクト系のコードを探す
    grep -rn "HTTP_REFERER" /path/to/wordpress --include="*.php"
    grep -rn "Location:" /path/to/wordpress --include="*.php"
    
    # 主要な.htaccessファイルを確認
    find /path/to/wordpress -name ".htaccess" -exec cat {} ; -print
    

    不正な.htaccessには以下のようなRewriteCondが含まれることが多いです:

    # 不正なリダイレクトの例
    RewriteCond %{HTTP_REFERER} (google|yahoo|bing) [NC]
    RewriteRule ^(.*)$ https://悪意のあるサイト/ [R=301,L]
    

    感染ファイルの一覧を作成する

    # 不審なコードを含むファイルを一覧出力
    grep -rln "eval.*base64_decode|HTTP_REFERER.*google" /path/to/wordpress > infected_files.txt
    cat infected_files.txt
    

    STEP 3:ファイルの完全削除(最重要!)

    これが今回の対応の最重要ステップです。

    スキャン&削除ではなく、サーバー内のファイルをすべて削除してクリーンな状態にします。

    なぜ「全削除」が必要なのか

    • 手口②で説明した通り、マルウェアは.min.css等に偽装して複数箇所にバックアップを持っている
    • 主要な感染箇所を手動で特定するのは現実的に不可能
    • 1ファイルでも残れば再感染する

    ファイルパーミッションを変更して書き込み防止

    削除前に、マルウェアが自己再生しないようパーミッションを変更します:

    # ディレクトリを書き込み不可にする
    find /path/to/wordpress -type d -exec chmod 555 {} ;
    
    # ファイルを書き込み不可にする
    find /path/to/wordpress -type f -exec chmod 444 {} ;
    

    サーバー内のファイルをすべて削除

    データベースは残し、ファイルを全削除します(記事データはDBに入っているため)。

    注意: 削除前に必ず uploads ディレクトリを退避してください。メディアファイルが失われます。


    STEP 4:WordPressを新規インストール

    クリーンな状態からWordPressを新規インストールします。

    感染前のDBは使い回さず、新しいデータベースを作成します。

    PHPバージョンのアップグレード

    PHPバージョンも最新版にアップグレードします。

    バージョン 状態
    PHP 7.4以前 セキュリティサポート終了(危険)
    PHP 8.0 サポート終了
    PHP 8.1 セキュリティサポート中
    PHP 8.2以降 推奨

    今回の事例ではPHP 7.4 → 8.2へアップグレードしました。

    wp-config.phpの設定

    // テーブルプレフィックスはランダムな文字列を使用(デフォルトのwp_は危険)
    $table_prefix = 'z6f4h_';
    
    // デバッグ情報を非表示に
    define( 'WP_DEBUG', false );
    define( 'WP_DEBUG_DISPLAY', false );  //画面表示を確実に無効化
    define( 'WP_DEBUG_LOG', true );       //ログ出力は有効(wp-content/debug.log)
    
    // PHP側の設定
    @ini_set( 'display_errors', 0 );
    @ini_set( 'log_errors', 1 );
    
    // 重複エラーを制限
    @ini_set( 'ignore_repeated_errors', 1 );
    @ini_set( 'ignore_repeated_source', 1 );
    
    // WarningとNoticeを除外(致命的エラーのみ表示)
    error_reporting( E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR );
    

    STEP 5:コンテンツの復旧

    新規インストールしたWordPressに記事・設定を復旧します。

    データベースの移行

    感染前のDBから記事データを新しいDBに移行します。ただし、DBに仕込まれたマルウェアコードに注意が必要です。

    # 旧DBをエクスポート
    mysqldump -u 旧ユーザー -p 旧DB名 > old_db.sql
    
    # DBの中に不審なコードがないか確認
    grep -n "base64_decode|eval|<script" old_db.sql
    grep -n "HTTP_REFERER|preg_replace" old_db.sql
    

    問題がなければ新DBにインポート:

    # テーブルプレフィックスを変換してインポート
    sed 's/wp_/z6f4h_/g' old_db.sql > converted_db.sql
    mysql -u new_wp_user -p new_wp_db < converted_db.sql
    

    テーマ・プラグインの再インストール

    感染前のテーマ・プラグインファイルは使わず、公式ソースから再インストールします。

    メディアファイルの復旧

    先ほど退避したuploadsフォルダを、悪意のあるファイル・偽装ファイルがないかチェック後戻します。


    STEP 6:セキュリティ強化

    復旧後は必ずセキュリティを強化します。

    .htaccessのセキュリティ設定

    # wp-config.phpへのアクセスを禁止
    <Files wp-config.php>
      Order allow,deny
      Deny from all
    </Files>
    
    # xmlrpc.phpへのアクセスを制限
    <Files xmlrpc.php>
      Order allow,deny
      Deny from all
    </Files>
    
    # phpファイルをuploadsディレクトリで実行禁止(重要!)
    <Directory /path/to/wordpress/wp-content/uploads>
      <Files *.php>
        Order allow,deny
        Deny from all
      </Files>
    </Directory>
    
    # debug.logへのアクセスを禁止
    <Files debug.log>
      Order allow,deny
      Deny from all
    </Files>
    
    # ディレクトリ一覧を非表示
    Options -Indexes
    

    Solid Securityの設定

    Solid Security(旧iThemes Security)プラグインで以下を設定します:

    • ログインURLの変更: /wp-login.php から推測しにくいURLへ変更
    • ブルートフォース保護: ログイン失敗回数に制限を設定
    • ファイル変更検知: ファイルが変更された際に通知
    • Google Safe Browsing連携: Googleのセーフブラウジングと連携
    • 脆弱性スキャン: プラグインの既知の脆弱性を定期チェック

    管理者アカウントの再設定

    - ユーザー名を「admin」以外に変更(攻撃者は「admin」を最初に試す)
    - 強力なパスワードを設定(16文字以上、記号含む)
    - メールアドレスを確認
    

    STEP 7:動作確認と監視体制の構築

    復旧後の確認チェックリスト

    □ Google・Yahoo・Bingで検索してリダイレクトが発生しないか
    □ スマートフォンからのアクセスでリダイレクトが発生しないか
    □ 外部ツール(Sucuri SiteCheck等)でクリーンと判定されるか
    □ Google Search Consoleにセキュリティ問題が表示されないか
    □ 全ページが正常に表示されるか
    □ 問い合わせフォームが正常に動作するか
    □ 管理画面にログインできるか
    □ 新しいログインURLで正常にアクセスできるか
    

    Googleへの審査依頼

    Googleのセーフブラウジングに登録されていた場合、Google Search Consoleから審査を申請します:

    1. Search Console → セキュリティの問題
    2. 「問題を修正しました」をクリック
    3. 審査をリクエスト

    審査は通常 数日〜1週間程度 かかります。

    今後の監視体制

    作業 頻度
    WordPressコア・プラグイン・テーマの更新 月1回(最重要)
    バックアップの取得 月1回以上
    Google Search Consoleのセキュリティタブ確認 月1回
    Solid Securityのログ確認 月1回
    不要プラグイン・テーマの削除 随時

    「スキャン&削除」が危険な理由

    一般的な対処法として紹介されている「マルウェアスキャナーで検出して削除する」方法では、今回のような高度な感染には対応できません。

    比較項目 スキャン&削除 完全削除&再インストール
    対応範囲 検知できたファイルのみ 主要なファイル
    難読化への対応 難しい(見落とし多数) 完全(削除するため)
    自己再生マルウェアへの対応 困難 完全
    再感染リスク 高い 低い
    作業時間 短い 長い
    確実性 低い 高い

    時間はかかっても、完全削除&再インストールが確実な方法です。


    まとめ

    WordPressマルウェア感染からの復旧は、スキャンツールに頼るだけでは不十分です。
    本記事で解説した完全削除&再インストールの手順こそが、再感染リスクを最小化できる方法です。

    復旧フローの要点まとめ:

    1. まず調査・バックアップ(感染状態の記録)
    2. マルウェアの手口を解析(リダイレクト方式・自己再生・難読化)
    3. ファイルを完全削除する(核オプション)←最重要
    4. 最新WordPressを新規インストール
    5. DBから記事データのみ移行、テーマ・プラグインは公式から再取得
    6. セキュリティを徹底強化してから公開
    7. Googleへ審査申請・監視体制を構築

    感染の原因の多くはプラグイン・WordPress本体の更新放置です。月に1回の定期更新と定期バックアップを習慣にすることが、最強のセキュリティ対策になります。

    関連記事

    WordPressへの侵入を防ぐ「予防」対策まとめ|ログイン・権限・設定の3本柱 WordPressへの不正ログインや改ざんを防ぐための予防的セキュリティ対策を解説。ログインURL変更・不要プラグイン削除・wp-config.php強化など、すぐに実践できる設定を網羅します。  続きを読む WordPressの異常を早期発見する「検知」対策まとめ|実際の感染事例から学ぶ手口と監視方法 WordPressへの侵入や改ざんをいち早く発見するための監視・検知対策を解説。ファイル変更検知・Google Search Console・セキュリティプラグインの活用に加え、実際の感染事例で確認された巧妙な隠蔽手口も紹介します。  続きを読む