WordPressを構築したらやるセキュリティ設定:外から見える情報を順に塞ぐ【SiteGuard・Jetpack・WPScan】

IT・セキュリティ検証
スポンサーリンク

はじめに

このブログは2020年から WordPress で運用しています。コメントスパム、検索結果を悪用したSEOスパム、管理画面に入れなくなるトラブルなど、何かが起きるたびに対策を足してきました。

この記事では、その積み重ねを「WordPressを構築したら最初にやるセキュリティ設定」として、まとめ直します。

これまでの記事

時期記事この記事で関係する章
2020年5月WordPressはスパム対策だけで十分かSTEP 4(コメントスパム)
2022年11月BackWPup4.0で重大なエラーが発生していたので調べてみたSTEP 2(バックアップ)
2025年10月WordPressにJetpackを導入してMarkdownで記事を書けるようにしてみたSTEP 5(Jetpackとの両立)
2026年2月WordPressの検索結果を悪用したSEOスパム攻撃|事象まとめと対処STEP 4(検索結果ページ)
2026年9月WordPressの管理画面に入れなくなったので乗っ取りを疑って切り分けてみたSTEP 3・4(プラグインの重ねがけ、ログインURLの漏れ)

方針

  1. 攻撃者と同じく「外」から見て、見えているものを塞ぐ
  2. セキュリティ系のプラグインは重ねすぎない(放置されたプラグインのせいで、自分が締め出されたことがあります)
  3. Jetpack を使うなら、XML-RPC と REST API は開けておく。その代わりに、別の対策を入れる
  4. 最後に WPScan で外から確認し、定期的に見直す

環境

項目内容
サーバーエックスサーバー
WordPress7.x
テーマCocoon(子テーマ)
セキュリティSiteGuard WP Plugin、エックスサーバーの WAF とWordPressセキュリティ設定
その他Jetpack(Markdown で記事を書くため)

STEP 1. まず外から見る

攻撃者は、まずサイトの外から「ログインURL」「ユーザー名」「開いている入口」「置きっぱなしのファイル」を調べます。同じことを自分でもやってみます。

次のスクリプトは、GET と、XML-RPC に「使える機能の一覧」を問い合わせるリクエストだけを送ります。必ず自分のサイトに対して実行してください。

#!/usr/bin/env bash
# 使い方: bash wp-outside-check.sh https://example.com
B=${1%/}
code() { curl -s -o /dev/null -w "%{http_code} %{redirect_url}" --max-time 15 "$B/$1"; }

echo "== ログインURLが漏れていないか =="
for p in wp-login.php wp-admin/ login admin dashboard; do
  printf "%-28s %s\n" "/$p" "$(code "$p")"
done
echo "-- ページ内のログインリンク --"
curl -sL --max-time 20 "$B/" | grep -oE 'href="[^"]*(wp-login|login_[0-9]+)[^"]*"' | sort -u

echo
echo "== ユーザー名が漏れていないか =="
printf "%-28s %s\n" "/?author=1" "$(code '?author=1')"
curl -s --max-time 15 "$B/wp-json/wp/v2/users" | grep -oE '"slug":"[^"]+"' | sed 's/^/REST API users     /'
POST=$(curl -s --max-time 15 "$B/wp-json/wp/v2/posts?per_page=1&_fields=link" | sed 's#\\/#/#g' | grep -oE 'https?://[^"]+' | head -1)
curl -s --max-time 15 "$B/wp-json/oembed/1.0/embed?url=$POST" | sed 's#\\/#/#g' | grep -oE '"author_url":"[^"]+"' | sed 's/^/oEmbed             /'

echo
echo "== XML-RPC =="
curl -s --max-time 15 -X POST -H 'Content-Type: text/xml' \
  --data '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>' \
  "$B/xmlrpc.php" | grep -oE 'wp\.getUsersBlogs|pingback\.ping' | sort -u | sed 's/^/有効なメソッド     /'

echo
echo "== 置きっぱなしのファイル =="
for p in wp-config.php.bak wp-config.php.save wp-config.php~ .env .git/config wp-content/debug.log readme.html wp-content/uploads/; do
  printf "%-28s %s\n" "/$p" "$(code "$p")"
done

echo
echo "== 検索結果ページの robots =="
curl -sL --max-time 15 "$B/?s=test" | grep -oiE "<meta name=.robots.[^>]*>"

結果の読み方

確認項目良い状態対策が必要な状態対策する章
/wp-login.php404200STEP 3
/wp-admin/404、またはトップページへ転送変更後のログインURLへ転送されるSTEP 3
ページ内のログインリンク何も出ないlogin_数字 などが出るSTEP 4
/?author=1トップページへ転送/author/ユーザー名/ へ転送されるSTEP 3
REST API users / oEmbedログインIDとは違う文字列ログインIDと同じ文字列STEP 5
XML-RPCpingback.ping が出ない(Jetpack を使わないなら XML-RPC 自体が止まっている)pingback.ping が出るSTEP 3
置きっぱなしのファイル404 / 403200削除する
検索結果ページnoindex何も付いていないSTEP 4

このブログでは、SiteGuard でログインURLを変更していたにもかかわらず、ログインURLが2か所から漏れていました

  • /wp-admin/ を開くと、誰でも変更後のログインURLに転送されていた
  • サイドバーの「メタ情報」ウィジェットの「ログイン」リンクに、変更後のログインURLが全ページで出ていた

STEP 2. サーバー側の設定(エックスサーバー)

WAF設定

サーバーパネルの「WAF設定」で、すべての項目を ON にします。記事の保存やプラグインの更新が誤って弾かれたときは、SiteGuard の「WAFチューニングサポート」で、その操作だけを除外します。

WordPressセキュリティ設定

項目設定注意
国外IPアクセス制限(ダッシュボード / XML-RPC API / REST API)ONJetpack からのアクセスは制限の対象外なので、Jetpack を使っていても ON でかまいません。海外から管理画面を使うときや、Cloudflare など海外を経由するサービスを使うときは OFF にする必要があります
ログイン試行回数制限ON
コメント・トラックバック制限ON

バックアップ

エックスサーバーは、1日1回自動でバックアップを取っています。保持されるのは、Webデータ・メール・MySQL とも過去14日分です(古いサーバーでは Web とメールが7日分)。

改ざんに気づくのが遅れると、14日分では足りないこともあります。自動バックアップだけに頼らず、プラグインなどで自分でもバックアップを取っておきましょう(参考:BackWPup4.0で重大なエラーが発生していたので調べてみた)。

STEP 3. SiteGuard WP Plugin の設定

機能設定ポイント
管理ページアクセス制限任意ログインしていない接続元から /wp-admin/ を守る
ログインページ変更ON +「管理者ページからログインページへリダイレクトしないチェックを入れないと、/wp-admin/ を開くだけで変更後のURLが分かる
画像認証ON他の CAPTCHA 系プラグインと重ねない
ログイン詳細エラーメッセージの無効化ONユーザー名とパスワードのどちらが違うのかを表示しない
ログインロックON何度も失敗した接続元を一時的に締め出す
ログインアラートON自分以外のログインにすぐ気づける
フェールワンス任意正しく入力しても1回目はわざと失敗する。慣れていないと「ログインできない」と焦る
XMLRPC防御Jetpack あり:ピングバック無効化 / なし:XMLRPC無効化XMLRPC無効化にすると Jetpack が動かなくなる
ユーザー名漏えい防御ON(Jetpack ありなら「REST API 無効化」は OFF)有効にしただけでは、/wp-json/wp/v2/users は開いたまま(STEP 5 で対策)
更新通知ON本体・プラグイン・テーマの更新をメールで知らせてくれる
WAFチューニングサポート必要なときだけWAF の誤検知を除外する

前回の教訓:SiteGuard の画像認証に加えて reCAPTCHA のプラグインも入れていたところ、そのプラグインが放置されていて検証が通らなくなり、正しいパスワードでもログインできなくなりました。詳しくは 管理画面に入れなくなった記事 にまとめています。

ログインURLを忘れたときの確認方法

「リダイレクトしない」を有効にすると、/wp-admin/ からログイン画面に飛べなくなります。URLを忘れたときは、次のどちらかで確認できます。

  • ファイル管理で public_html/.htaccess を開き、SiteGuard の記述の中にある login_ で始まる名前を見る
  • phpMyAdmin で次のSQLを実行し、renamelogin_path の値を見る
SELECT option_name, option_value FROM wp_options WHERE option_name LIKE 'siteguard%';

STEP 4. WordPress本体・テーマの設定

「メタ情報」ウィジェットを置かない

「ログイン」リンクに、変更後のログインURLがそのまま出ます。「外観」→「ウィジェット」から削除します。読者に必要なリンクはありません。

ユーザー登録を閉じる

「設定」→「一般」で、次のようにします。

  • 「だれでもユーザー登録ができるようにする」:OFF
  • 「新規ユーザーのデフォルト権限グループ」:購読者

ログインIDと表示名

  • ログインIDに admin など推測されやすい名前を使わない
  • 「ユーザー」→「プロフィール」の「ブログ上の表示名」を、ログインIDとは別の名前にする

管理画面からのファイル編集を無効にする

乗っ取られたときに、管理画面からテーマやプラグインのファイルを書き換えられないようにします。wp-config.php の「編集が必要なのはここまでです」の行より上に追加します。

define( 'DISALLOW_FILE_EDIT', true );

更新と、プラグインの整理

  • 「ダッシュボード」→「更新」で本体の自動更新の設定を確認し、プラグインとテーマは一覧の「自動更新を有効化」から設定する
  • 使っていないプラグイン・テーマは、停止ではなく削除する
  • WordPress.org の「最終更新」と「検証済み」のバージョンを見て、放置されたプラグインは入れない

検索結果ページを noindex にする

検索結果ページが検索エンジンに登録されると、SEOスパムに悪用されることがあります。STEP 1 のスクリプトで noindex が付いているか確認します(参考:WordPressの検索結果を悪用したSEOスパム攻撃)。

コメントスパム対策

Akismet などを使います(参考:WordPressはスパム対策だけで十分か)。

後回しでよいもの

  • readme.html:削除しても本体の更新で戻ってきます。今の WordPress はバージョンが載っていないので、後回しでかまいません
  • 外からの WP-Cron 実行:気になる場合は、DISABLE_WP_CRON を設定し、サーバーの Cron から実行する方式に変えます

STEP 5. Jetpack を使うなら:XML-RPC と REST API は開けておく。その代わりに

Jetpack は、WordPress.com との通信に XML-RPC を使います。SiteGuard で XML-RPC を完全に止めたり、REST API をまとめて止めたりすると、Jetpack の統計などが動かなくなります。そこで、入口は開けたまま、別の方法で守ります。

開けておくもの何をされるかその代わりの対策
XML-RPCパスワード総当たりの入口になる(このブログにも海外から来ていました)SiteGuard の「ピングバック無効化」、Jetpack の「ブルートフォース攻撃からの保護」、エックスサーバーの国外IPアクセス制限(Jetpack は対象外)、長く推測されにくいパスワード
REST API(/wp-json/wp/v2/usersユーザーのスラッグが見えるスラッグ(user_nicename)をログインIDと別の文字列にする

XML-RPC 経由のログインにはログイン画面がないので、SiteGuard の画像認証は効きません。パスワードの強さと、総当たりへの対策が守りの中心になります。

スラッグをログインIDと別の文字列にする

WordPress のユーザーには、ログインIDとは別に、URLなどに使う「スラッグ(user_nicename)」があります。初期状態ではログインIDと同じ文字列なので、REST API・oEmbed・記事の著者リンクからログインIDが分かってしまいます。

phpMyAdmin で次のように変えると、この3か所がまとめて直ります。ログインIDは変わりません。

UPDATE wp_users SET user_nicename = '公開してよい名前' WHERE ID = 1;
  • 著者ページのURLが /author/公開してよい名前/ に変わります(古いURLは404になります)
  • ログインIDとは必ず別の文字列にしてください

すでにログインIDが知られている場合

ログイン履歴に、実際のログインIDを使った総当たりが残っているなら、ログインIDそのものを変えることも考えます。

UPDATE wp_users SET user_login = '新しいログインID' WHERE ID = 1;
  • 実行するとログアウトされます。実行する前に、ログインURLを控えておいてください
  • 以降は、新しいログインIDかメールアドレスでログインします

Jetpack のブルートフォース保護

「Jetpack」→「設定」→「セキュリティ」の「ブルートフォース攻撃からの保護」が ON になっているか確認します(初期状態は ON)。自分が締め出されないように、よく使うIPアドレスを「常に許可するIPアドレス」に登録しておけます。

STEP 6. WPScan で外から確認する

設定が終わったら、WPScan で外から確認します。Docker で実行すれば、Mac に Ruby の環境を用意する必要はありません。

docker run --rm wpscanteam/wpscan:latest \
  --url https://example.com/ \
  --enumerate ap,cb,dbe,u1-10 \
  --plugins-detection passive \
  --plugins-version-detection mixed \
  --throttle 300 \
  --format cli-no-color --no-banner
オプション意味
--enumerate ap,cb,dbe,u1-10プラグイン、設定ファイルのバックアップ、DBダンプ、ユーザー(ID 1〜10)を調べる
--plugins-detection passiveページ内の情報からだけプラグインを探す(総当たりしない)
--throttle 300リクエストの間隔を 0.3 秒空ける
  • パスワードの総当たり(--passwords)はやりません。 SiteGuard のログインロックで、自分のIPが締め出されるだけです
  • 強くスキャンしすぎると、自宅のIPがサーバーの WAF に弾かれることがあります
  • ページに痕跡を残さないプラグイン(SiteGuard など)は、パッシブ検出では見つかりません。更新状況は管理画面で確認します
  • 既知の脆弱性(CVE)と照合するには、WPScan で無料のAPIトークン(1日25回まで)を取得し、--api-token "$WPSCAN_API_TOKEN" を付けて実行します

このブログで実行したときの結果は、次のとおりでした。

項目結果
設定ファイルのバックアップ / DBダンプなし
WordPress 本体のバージョン外から特定できない
プラグインすべて最新
ユーザー名REST API から取れた → STEP 5 で対策
XML-RPC有効(Jetpack のために残す)
外からの WP-Cron 実行 / readme.html有効 / 公開(優先度は低い)

STEP 7. 月に1回の見直し

  • STEP 1 のスクリプトを実行する
  • SiteGuard のログイン履歴で、ログインに成功した記録に知らないIPがないか確認する
SELECT * FROM wp_siteguard_history WHERE operation = 0 ORDER BY id DESC LIMIT 10;
  • APIトークンを付けて WPScan を実行する
  • Googlebot や Google 検索から来た人にだけ別のページを見せる改ざんがないか確認する(手順は 管理画面に入れなくなった記事 にあります)
  • プラグインの「最終更新」を見直し、使っていないものは削除する

まとめ:チェックリスト

#場所やることJetpack を使う場合
1サーバーWAF をすべて ON同じ
2サーバー国外IPアクセス制限を ONON でよい(Jetpack は対象外)
3サーバーバックアップの保持期間を確認し、自前でも取る同じ
4SiteGuardログインページ変更 +「リダイレクトしない」同じ
5SiteGuard画像認証・ログインロック・ログインアラート・詳細エラーメッセージの無効化同じ
6SiteGuardXMLRPC防御ピングバック無効化にとどめる
7SiteGuardユーザー名漏えい防御REST API 無効化は OFF
8WordPress「メタ情報」ウィジェットを置かない同じ
9WordPressユーザー登録を閉じる同じ
10WordPressスラッグをログインIDと別の文字列にする必須
11WordPressDISALLOW_FILE_EDIT同じ
12WordPress自動更新、使っていないプラグインの削除同じ
13Jetpackブルートフォース攻撃からの保護を ON
14確認外から確認するスクリプト、WPScan同じ
15運用月に1回の見直し同じ

セキュリティ対策は、入れて終わりではありません。外から見て、塞いで、また外から確認するの繰り返しです。プラグインを足すほど、障害の原因になりうる場所も増えます。サーバーの機能と SiteGuard を中心に、必要なものだけに絞るのがおすすめです。

参考

コメント

タイトルとURLをコピーしました