トラブルシューティング for WordPress

エラー対処と人気プラグイン・テーマ解説

[wpseo_breadcrumb]

普通の対策では守れない。プロが実務でやっているWordPressセキュリティ

普通の対策では守れない。プロが実務でやっているWordPressセキュリティのアイキャッチ画像

1. wp-config.php を別ディレクトリへ移動

■ なぜ有効?

wp-config.php にはDB情報や認証キーが入っており、最重要ファイルです。 WordPressは ドキュメントルートの1階層上のディレクトリも自動的に探す仕様 があるため、Webから直接アクセスできない場所に置きます。

■ 例

/home/user/
  ├─ wp-config.php          ← ここに移動(Webから直接アクセス不可)
  └─ public_html/
      └─ wordpress本体

■ wp-config.php内で修正が必要な箇所

wp-config.phpを移動した場合、パスの定義を修正する必要があります。

// wp-config.php の先頭付近に追加

/** 絶対パス設定 */
if ( !defined('ABSPATH') ) {
    define('ABSPATH', dirname(__FILE__) . '/public_html/');
}

/** wp-content ディレクトリのパス(カスタマイズしている場合) */
// define('WP_CONTENT_DIR', dirname(__FILE__) . '/public_html/wp-content');
// define('WP_CONTENT_URL', 'https://example.com/wp-content');

/** プラグインディレクトリのパス(カスタマイズしている場合) */
// define('WP_PLUGIN_DIR', dirname(__FILE__) . '/public_html/wp-content/plugins');
// define('WP_PLUGIN_URL', 'https://example.com/wp-content/plugins');

重要なポイント:

  • ABSPATH は WordPress本体がインストールされているディレクトリを指す
  • 移動先の階層に応じて dirname(__FILE__) . '/public_html/' の部分を調整
  • 通常は ABSPATH の定義だけで動作するが、wp-contentを別の場所に配置している場合は追加定義が必要

■ 確認方法

移動後、以下をチェック:

// デバッグ用(確認後は削除)
echo ABSPATH; // WordPressのルートパスが正しいか確認

■ 注意点

  • 移動できるのは1階層上のみ(2階層以上上には置けない)
  • シンボリックリンクを使う場合は別の考慮が必要
  • サーバー設定によっては動作しない場合がある
  • 移動後は必ず動作確認を行う(管理画面へのログイン、メディアアップロードなど)

■ 応用:wp-content も公開ディレクトリ外に配置(最高レベル)

さらにセキュリティを高める場合、wp-contentディレクトリも移動できます。

/home/user/
  ├─ wp-config.php
  ├─ wp-content/           ← テーマ・プラグインも外に出す
  │   ├─ plugins/
  │   ├─ themes/
  │   └─ uploads/
  └─ public_html/
      └─ wordpress本体(wp-contentは空またはダミー)

wp-config.phpに追加:

// wp-content の場所を変更
define('WP_CONTENT_DIR', dirname(__FILE__) . '/wp-content');
define('WP_CONTENT_URL', 'https://example.com/wp-content');

// uploads ディレクトリだけは公開ディレクトリ内に戻す場合
// define('UPLOADS', 'public_html/wp-content/uploads');

注意: この設定は高度で、.htaccessやNginx設定での静的ファイル配信設定が別途必要になります。

■ 追加でやると強い

// wp-config.php の先頭付近
define('DISALLOW_FILE_EDIT', true);  // テーマ・プラグインエディタを無効化
define('DISALLOW_FILE_MODS', true);  // プラグイン・テーマのインストール/更新/削除を無効化

// デバッグモードは本番環境では必ずfalse
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);

// データベーステーブルの接頭辞をデフォルトから変更(新規構築時のみ推奨)
// $table_prefix = 'wp_';  ← これを変更
// $table_prefix = 'xyz_'; のようにランダムな文字列に

重要: DISALLOW_FILE_MODS を有効にすると、管理画面からのプラグイン/テーマのインストール・更新・削除がすべて不可になります。FTPやSSHでの作業が必要になるため、運用体制を整えてから実装しましょう。


2. ファイル編集機能の完全無効化(管理画面対策)

管理画面からテーマ/プラグインを改ざんされるケースは多いです。

define('DISALLOW_FILE_EDIT', true);

これだけで 侵入後の被害拡大を大幅に抑制 できます。 実務では「初期設定に入れるレベル」の重要度です。

■ 効果

  • テーマエディタが非表示になる
  • プラグインエディタが非表示になる
  • 管理者権限でもファイル編集不可

3. XML-RPCの機能単位ブロック

昔は「xmlrpc.php を丸ごと無効(全停止)」が主流でしたが、今は少し進化しています。

■ なぜ?

Jetpackや一部連携機能がXML-RPCを利用するため、全停止は業務上不便な場合があります

■ 推奨:機能制限

WordPressフィルタで ブルートフォースに使われる機能だけ無効化

// functions.php または専用プラグイン
add_filter('xmlrpc_methods', function($methods){
    // ブルートフォース攻撃で悪用されやすいメソッド
    unset($methods['wp.getUsersBlogs']);
    unset($methods['system.multicall']);
    unset($methods['system.listMethods']);
    return $methods;
});

■ 完全に使わない場合

// XML-RPC 完全無効化
add_filter('xmlrpc_enabled', '__return_false');

または .htaccess で

<Files xmlrpc.php>
    Order Deny,Allow
    Deny from all
</Files>

4. REST API の不要エンドポイント制限

REST API からユーザー情報を取得できるのは地味に危険です。

■ ユーザー列挙対策

// /wp-json/wp/v2/users を無効化
add_filter('rest_endpoints', function ($endpoints) {
    if (isset($endpoints['/wp/v2/users'])) {
        unset($endpoints['/wp/v2/users']);
    }
    if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) {
        unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
    }
    return $endpoints;
});

■ より厳格な対策:ログインユーザーのみに制限

add_filter('rest_authentication_errors', function($result) {
    if (!is_user_logged_in()) {
        return new WP_Error(
            'rest_not_logged_in',
            'REST APIへのアクセスにはログインが必要です。',
            array('status' => 401)
        );
    }
    return $result;
});

これで ユーザーID総当たり攻撃の足がかりを遮断 できます。


5. プラグイン・テーマの存在秘匿

攻撃者はまず「どのプラグインが入っているか」を調べます。

■ 対策例

1. バージョン情報削除

// functions.php
remove_action('wp_head', 'wp_generator');

// CSS/JSのバージョン情報も削除
add_filter('style_loader_src', 'remove_version_from_assets', 9999);
add_filter('script_loader_src', 'remove_version_from_assets', 9999);

function remove_version_from_assets($src) {
    if (strpos($src, 'ver=')) {
        $src = remove_query_arg('ver', $src);
    }
    return $src;
}

2. ディレクトリリスティング無効化

.htaccess に追加

# ディレクトリ一覧表示を無効化
Options -Indexes

# wp-content へのアクセス制限
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-content/plugins/.*\.php$ - [F,L]
RewriteRule ^wp-content/themes/.*\.php$ - [F,L]
</IfModule>

3. 不要プラグインは削除(停止だけではダメ)

停止しているだけでは、ファイルが残っているため脆弱性スキャンの対象になります。


6. PHP実行制限(wp-content/uploads)

画像アップロード用ディレクトリでPHPが実行できると即終了です。

■ .htaccess(Apache)

wp-content/uploads/.htaccess を作成

# PHP実行を完全禁止
<FilesMatch "\.(php|php3|php4|php5|phtml|pl|py|jsp|asp|sh|cgi)$">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
    <IfModule !mod_authz_core.c>
        Order deny,allow
        Deny from all
    </IfModule>
</FilesMatch>

■ Nginx の場合

location ~* ^/wp-content/uploads/.*\.php$ {
    deny all;
}

これは 実際の改ざん被害を止める超実戦的対策 です。


7. 認証キーの定期ローテーション

WordPressのセキュリティキーは「設定して終わり」ではなく、 漏洩前提でローテーションする思想が本来は正しい です。

■ 方法

  1. 公式APIで新しいキーを取得
  2. wp-config.php の以下の8つのキーを更新
define('AUTH_KEY',         'ここに新しいキー');
define('SECURE_AUTH_KEY',  'ここに新しいキー');
define('LOGGED_IN_KEY',    'ここに新しいキー');
define('NONCE_KEY',        'ここに新しいキー');
define('AUTH_SALT',        'ここに新しいキー');
define('SECURE_AUTH_SALT', 'ここに新しいキー');
define('LOGGED_IN_SALT',   'ここに新しいキー');
define('NONCE_SALT',       'ここに新しいキー');
  1. 全ユーザー強制ログアウト(セッション無効化)

■ 実運用での注意

  • ローテーション後は全ユーザーが再ログイン必要
  • メンテナンス時間帯に実施
  • ユーザーへの事前告知を推奨
  • 推奨頻度:3〜6ヶ月に1回、または漏洩の疑いがある場合は即座に

8. ログインURL変更よりも重要な「レート制限」

wp-login.php のURL変更はセキュリティ through obscurity(隠蔽による安全性) で気休めレベルです。 本質は 試行回数制限か2要素認証です。

3つの方法を紹介します。

■ 1. サーバー側で制御

fail2ban の設定例(推奨)

# /etc/fail2ban/filter.d/wordpress.conf
[Definition]
failregex = ^<HOST> .* "POST /wp-login.php
            ^<HOST> .* "POST /xmlrpc.php

# /etc/fail2ban/jail.local
[wordpress]
enabled = true
filter = wordpress
logpath = /var/log/apache2/access.log
maxretry = 5
bantime = 3600

その他のオプション:

  • WAF(Web Application Firewall)
  • mod_security
  • CloudflareのRate Limiting

■ 2. アプリ側

プラグインで対応:

  • Limit Login Attempts Reloaded
  • WP Limit Login Attempts

ボットの総当たりは”防ぐ”より”弾く”が正解 です。

■ 3. 2要素認証(2FA)の導入

ログインセキュリティを本気で強化するなら:

  • Google Authenticator
  • WP 2FA プラグイン
  • Two Factor Authentication

9. 自動更新をあえて止める判断(上級者向け)

一般的には自動更新推奨ですが、 企業サイト・会員制サイト・カスタマイズが多いサイトでは「即時自動更新=リスク」になる場合があります

■ 自動更新を無効化

// すべての自動更新を停止
define('AUTOMATIC_UPDATER_DISABLED', true);

// または細かく制御
define('WP_AUTO_UPDATE_CORE', false);  // コア更新を無効化
add_filter('auto_update_plugin', '__return_false');  // プラグイン
add_filter('auto_update_theme', '__return_false');   // テーマ

■ 推奨運用フロー

  1. ステージング環境で更新テスト
  2. 互換性・動作確認
  3. 問題なければ本番環境へ手動更新
  4. バックアップは必須

これが「壊れないセキュリティ運用」です。

■ ただし注意

  • セキュリティパッチの適用が遅れるリスクがある
  • 手動更新を忘れると脆弱性が放置される
  • 専任の担当者がいる場合のみ推奨

まとめ:守る=設定+構造+運用

WordPressのセキュリティは「プラグインを入れれば終わり」ではありません。

3層の防御が必要

  1. 設定層:wp-config.php、定数定義
  2. 構造層:ファイル配置、権限設定、サーバー設定
  3. 運用層:定期更新、監視、バックアップ

今日から実装できる優先順位

【高】即実装推奨

  • ファイル編集機能の無効化(DISALLOW_FILE_EDIT)
  • uploads での PHP実行禁止
  • REST API ユーザー列挙対策

【中】環境に応じて実装

  • wp-config.php の移動
  • XML-RPC の機能制限
  • 認証キーのローテーション

【低】上級者・専任担当者がいる場合

  • 自動更新の無効化(ステージング運用)

セキュリティは一度設定して終わりではなく、継続的な改善が必要です。