2026年9月28日

2026年9月28日

WordPressのファイル改ざん検知を自動化してアラートを受け取る設定

WordPressのファイル改ざん検知は、「公式配布物との照合」と「自分で取った基準(ハッシュ値)との比較」の2つを定期実行し、差分があったときだけ通知する形にすると、手間をかけずに続けられます。この記事では、WP-CLI のチェックサム検証と sha256 のハッシュ比較を cron で回してメールやチャットに通知する方法と、プラグインで行う場合の選び方、通知が来たときの初動を説明します。

改ざん検知の考え方

改ざんの多くは、既存のPHPファイルへのコード挿入、wp-content/uploads などへの不審なPHPファイルの設置、.htaccess の書き換えといった形で現れます。検知の方法は大きく3つあります。

  • 公式配布物との照合: WordPress本体と、wordpress.org で配布されているプラグインは、版ごとのファイルのチェックサムが公開されています。手元のファイルと比べれば、書き換えや余計なファイルが分かります。
  • 基準ハッシュとの比較: テーマや有料プラグイン、自作コードは公式のチェックサムがありません。正常な状態のハッシュ値を保存しておき、定期的に比べます。
  • リアルタイム監視: inotify などでファイルの変更を即座に拾う方法です。精度は高い一方、更新やキャッシュ生成でも反応するため、通知の絞り込みが必要です。

ここでは、多くのサーバーで動き、誤検知も調整しやすい前の2つを組み合わせます。

方法1: WP-CLIで公式配布物と照合する

WP-CLI には本体とプラグインのチェックサムを検証するコマンドがあります。まずは手動で実行し、どんな結果が出るかを確認してください。

cd /var/www/example.com/public
wp core verify-checksums
wp plugin verify-checksums --all

wp core verify-checksums は、本体ファイルが書き換えられていれば「File doesn't verify against checksum」、本来無いファイルがあれば「File should not exist」と表示し、問題があると終了コードが 0 以外になります。

wp plugin verify-checksums --all は、wordpress.org で配布されているプラグインだけが対象です。有料プラグインや自作プラグインはチェックサムを取得できずにスキップされ、そのせいで終了コードが 0 にならないことがあります。対象外のものは --exclude=プラグインのスラッグ で外すか、方法2のハッシュ比較で見ます。テーマには同等のコマンドが用意されていないため、テーマも方法2で扱います。

実行ユーザーは、Webサーバーと同じユーザー(例: www-data)か、読み取り権限のある専用ユーザーにします。root で実行するために --allow-root を付ける運用は避けてください。

方法2: 基準ハッシュを取り、差分を比べる

正常な状態でファイルごとの sha256 ハッシュを記録し、次回以降の結果と比べます。アップロード画像やキャッシュのように日常的に変わる場所は除外し、そのかわり「uploads 内のPHPファイル」は別に探します。画像置き場にPHPがあることは、通常の運用ではほとんどないためです。

基準ファイルの保存場所は、Webから見えない場所(ドキュメントルートの外)にし、Webサーバーのユーザーが書き換えられない権限にします。攻撃者がファイルと基準の両方を書き換えられると、検知が働かないためです。

監視スクリプトの例

方法1と2をまとめて実行し、問題があったときだけ通知するスクリプトです。パスや通知先は環境に合わせて変更します。

#!/usr/bin/env bash
# /usr/local/bin/wp-integrity-check.sh
set -uo pipefail

WP_PATH="/var/www/example.com/public"
STATE_DIR="/var/lib/wp-integrity"          # ドキュメントルートの外
MAIL_TO="admin@example.com"
WEBHOOK_URL="${WP_INTEGRITY_WEBHOOK:-}"     # 例: チャットの Incoming Webhook。未設定なら送らない

cd "$WP_PATH" || exit 1
mkdir -p "$STATE_DIR"
report=""

# 1. 本体とプラグインを公式チェックサムと照合
if ! out=$(wp core verify-checksums 2>&1); then
  report+=$'\n[core]\n'"$out"
fi
if ! out=$(wp plugin verify-checksums --all 2>&1); then
  report+=$'\n[plugins]\n'"$out"
fi

# 2. wp-content(uploads・cache を除く)とルートの設定ファイルのハッシュ比較
current="$STATE_DIR/current.sha256"
baseline="$STATE_DIR/baseline.sha256"
{
  find wp-content \( -path 'wp-content/uploads' -o -path 'wp-content/cache' \) -prune -o -type f -print0
  find . -maxdepth 1 -type f \( -name 'wp-config.php' -o -name '.htaccess' -o -name 'index.php' \) -print0
} | sort -z | xargs -0 sha256sum > "$current"

if [ -f "$baseline" ]; then
  if ! out=$(diff "$baseline" "$current"); then
    report+=$'\n[hash diff]\n'"$out"
  fi
else
  cp "$current" "$baseline"
fi

# 3. uploads 内の PHP ファイル
if out=$(find wp-content/uploads -type f -iname '*.php' 2>/dev/null) && [ -n "$out" ]; then
  report+=$'\n[php in uploads]\n'"$out"
fi

# 通知(問題があるときだけ)
if [ -n "$report" ]; then
  printf '%s\n' "$report" | mail -s "[WP integrity] $(hostname) で差分を検出" "$MAIL_TO"
  if [ -n "$WEBHOOK_URL" ]; then
    payload=$(printf '%s' "WP integrity: $(hostname) で差分を検出しました。メールを確認してください。" | jq -Rs '{text: .}')
    curl -fsS -X POST -H 'Content-Type: application/json' -d "$payload" "$WEBHOOK_URL" >/dev/null
  fi
fi

メール送信には mail コマンド(mailutils などのパッケージ)が、チャット通知には jq と curl が必要です。チャットには差分の中身ではなく「検出した」という事実だけを送り、詳細はメールで見る形にしています。ファイルのパスにはサイト構成の情報が含まれるためです。

最初の実行で基準ファイルができます。基準は「正常な状態」でなければ意味がないため、初回は方法1の結果に問題がないこと、uploads にPHPがないことを確認してから採用してください。不安があれば、いったん baseline.sha256 を削除し、確認後にもう一度実行して作り直します。

cronで定期実行する

WP-Cron はサイトへのアクセスをきっかけに動く仕組みで、サイトが乗っ取られると止められる可能性もあります。監視はWordPressの外側、サーバーの cron で動かします。

# crontab -e(実行ユーザーで設定)。1時間ごと、重複起動は flock で防ぐ
0 * * * * flock -n /tmp/wp-integrity.lock /usr/local/bin/wp-integrity-check.sh

チェックの間隔は、サイトの規模とサーバー負荷で決めます。ファイル数が多いとハッシュ計算に時間がかかるため、まず手動で所要時間を測ってから設定してください。

更新のたびに出る誤検知への対処

プラグインやテーマを更新すると、ハッシュ比較は正常な変更も差分として報告します。運用で次のどちらかに決めておきます。

  • 更新作業の後に、方法1で問題がないことを確認してから cp current.sha256 baseline.sha256 で基準を更新する
  • 自動更新を使っている場合は、更新時刻と通知時刻を見比べ、差分の対象が更新されたプラグインのフォルダだけかを確認する

「通知が多すぎて見なくなる」のが一番の失敗です。除外するフォルダ(キャッシュ、ログ、バックアップ置き場など)を最初に洗い出してください。

プラグインで行う場合

サーバーにSSHで入れない共有ホスティングでは、セキュリティプラグインのファイルスキャン機能を使います。

  • Wordfence Security: 本体・テーマ・プラグインのファイルを wordpress.org の配布版と比べるスキャンと、メールでの通知機能があります。
  • Sucuri Security: 本体ファイルの整合性チェックや、セキュリティ上の出来事の記録・通知機能があります。

どちらも無料版と有料版で機能やスキャンの頻度が異なり、内容は変わることがあります(2026年10月現在)。導入前に公式サイトで無料版の範囲を確認してください。プラグインはWordPressの中で動くため、乗っ取られたサイトでは検知そのものが止められる可能性があります。可能であれば、サーバー側の監視と併用すると安心です。

通知が来たときの初動

  1. あわてて削除せず、差分の内容(どのファイルが、いつ変わったか)を記録します。
  2. 該当ファイルを開き、見覚えのない難読化されたコード(eval と base64_decode の組み合わせなど)がないかを確認します。
  3. 改ざんと判断したら、本体は同じ版を wp core download --version="$(wp core version)" --force --skip-content で上書きし(余分なファイルは残るので方法1の結果を見て個別に削除)、プラグインは公式版の再インストールで戻し、それ以外はバックアップから復元します。
  4. 管理者のパスワード、データベースのパスワード、wp-config.php の認証用キーを変更し、侵入口(古いプラグインなど)を特定して更新します。

まとめ

  • 本体とwordpress.org配布のプラグインは wp core verify-checksums と wp plugin verify-checksums で照合する
  • テーマ・有料プラグイン・設定ファイルは基準ハッシュとの比較で見る。uploads 内のPHPは別に探す
  • 基準ファイルはドキュメントルートの外に置き、監視はサーバーの cron で動かす
  • 更新後の基準の取り直し方を決め、除外対象を整理して誤検知を減らす
  • 通知が来たら記録してから判断し、公式版の再インストールかバックアップで戻す

お気軽にご相談ください

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