2026年6月2日

2026年6月2日

WordPressマルチサイトのセキュリティを強化する方法

はじめに

WordPressマルチサイトは1つのWordPressインストールで複数サイトを管理できますが、1か所の脆弱性がネットワーク全体に影響します。スーパー管理者権限の管理とサイト間の適切な分離が重要です。

症状・原因

  • マルチサイトのサブサイトが不正に作成されている
  • スーパー管理者権限が不必要なユーザーに付与されている
  • あるサブサイトのマルウェアがネットワーク全体に波及した
  • ユーザー登録を悪用されてスパムサイトが大量生成されている

解決手順

ステップ1:スーパー管理者の権限を最小化する

// wp-config.php または network-wide functions.php

// スーパー管理者の一覧を確認・管理
function audit_super_admins(): void {
    $super_admins = get_super_admins(); // スーパー管理者ユーザー名の配列

    foreach ($super_admins as $username) {
        $user = get_user_by('login', $username);
        if (!$user) {
            // 存在しないユーザーをスーパー管理者から削除
            revoke_super_admin($user->ID);
            error_log("[Multisite] Removed ghost super admin: $username");
        }
    }
}
add_action('admin_init', 'audit_super_admins');

// スーパー管理者の追加を制限(特定IPからのみ許可)
add_filter('grant_super_admin', function(bool $grant, int $user_id): bool {
    $allowed_ips = ['203.0.113.1']; // 管理者のIPアドレス
    if (!in_array($_SERVER['REMOTE_ADDR'] ?? '', $allowed_ips, true)) {
        error_log("[Multisite] Super admin grant blocked for user $user_id from " . ($_SERVER['REMOTE_ADDR'] ?? 'unknown'));
        return false;
    }
    return $grant;
}, 10, 2);

ステップ2:新規サイト登録を制限する

// wp-config.php: マルチサイト登録設定
// 'all'=誰でも登録可 / 'none'=登録不可 / 'blog'=サイトのみ / 'user'=ユーザーのみ
// define('NOBLOGREDIRECT', 'https://example.com'); // 未登録ドメインをリダイレクト

// functions.php: サイト作成を承認制にする
add_action('wpmu_new_blog', function(int $blog_id, int $user_id): void {
    // 新規サイトを即座にアーカイブ(停止)
    update_blog_status($blog_id, 'archived', 1);

    // 管理者に承認リクエストを送信
    $admin_email = get_site_option('admin_email');
    $site_url    = get_blog_option($blog_id, 'siteurl');
    wp_mail(
        $admin_email,
        '[要承認] 新規サイト登録: ' . $site_url,
        "新規サイトが登録されました。\nURL: $site_url\n管理画面で承認してください。"
    );
}, 10, 2);

ステップ3:ネットワーク全体にセキュリティポリシーを適用する

// mu-plugins/network-security.php: 全サブサイトに自動適用

// ① 全サイトで wp-config.php の直接アクセスを禁止
add_action('init', function(): void {
    if (!is_admin()) return;

    // 全サイトで wp_debug が本番環境でOFFになっているか確認
    if (defined('WP_DEBUG') && WP_DEBUG && !defined('WP_ENVIRONMENT_TYPE')) {
        error_log('[Multisite Security] WP_DEBUG is ON on site: ' . get_bloginfo('url'));
    }
});

// ② ネットワーク全体のログイン試行制限
add_filter('authenticate', function($user, string $username): mixed {
    $ip  = $_SERVER['REMOTE_ADDR'] ?? '';
    $key = 'login_fail_net_' . md5($ip);
    $fails = (int) get_site_transient($key); // ネットワーク全体のトランジェント

    if ($fails >= 5) {
        return new WP_Error('too_many_attempts', 'ログイン試行回数が上限を超えました。');
    }
    return $user;
}, 30, 2);

add_action('wp_login_failed', function(): void {
    $ip  = $_SERVER['REMOTE_ADDR'] ?? '';
    $key = 'login_fail_net_' . md5($ip);
    $fails = (int) get_site_transient($key);
    set_site_transient($key, $fails + 1, HOUR_IN_SECONDS);
});

ステップ4:サブサイト間のファイルアクセスを分離する

// mu-plugins/network-security.php: サブサイト間の分離強化

// アップロードディレクトリをサブサイトごとに分離(デフォルト動作の確認)
add_filter('upload_dir', function(array $dirs): array {
    // マルチサイトではデフォルトで /wp-content/uploads/sites/{id}/ に分離済み
    // 追加セキュリティ: 他サイトのアップロードパスへの直接アクセスをチェック
    $current_site_id = get_current_blog_id();
    $upload_path     = $dirs['basedir'];

    // 現在のサイトのパスに正しいIDが含まれているか確認
    if (is_multisite() && strpos($upload_path, "/sites/$current_site_id") === false) {
        error_log("[Multisite] Upload path mismatch on site $current_site_id: $upload_path");
    }
    return $dirs;
});

// .htaccess でサイト間のアップロードアクセスを制限
// /wp-content/uploads/sites/ ディレクトリの .htaccess:
/*
<FilesMatch "\.(php|php5|phtml|pl|py|cgi)$">
    Require all denied
</FilesMatch>
*/

ステップ5:ネットワーク全体のセキュリティ監査ログを実装する

// mu-plugins/network-audit-log.php

// スーパー管理者ログインを記録
add_action('wp_login', function(string $username, WP_User $user): void {
    if (!is_super_admin($user->ID)) return;

    $log = sprintf(
        '[%s] Super admin login: %s from %s on site %s',
        current_time('Y-m-d H:i:s'),
        $username,
        $_SERVER['REMOTE_ADDR'] ?? 'unknown',
        network_site_url()
    );
    error_log($log);

    // ネットワーク管理者にメール通知
    wp_mail(
        get_site_option('admin_email'),
        '[Multisite] スーパー管理者ログイン: ' . $username,
        $log
    );
}, 10, 2);

// サイトの作成・削除を記録
add_action('wpmu_new_blog',    fn($id) => error_log("[Multisite] Site created: $id"), 10, 1);
add_action('wpmu_delete_blog', fn($id) => error_log("[Multisite] Site deleted: $id"), 10, 1);

注意事項

  • grant_super_admin フィルターはネットワーク管理画面からの操作にのみ適用されます。wp-cli や直接DBへの操作はバイパスされるため、DBアクセス制限も合わせて設定してください
  • wpmu_new_blog でアーカイブ設定した場合、ネットワーク管理画面→サイト一覧から手動で「有効化」する必要があります
  • mu-plugins/ に配置したファイルは全サブサイトで自動的に読み込まれます。バグがあるとネットワーク全体に影響するため、十分にテストしてください

まとめ

マルチサイトセキュリティは①get_super_admins()で権限監査・IP制限でスーパー管理者追加を制御、②wpmu_new_blogフックで新規サイトを即座にアーカイブして承認制に変更、③mu-plugins/にネットワーク全体ポリシーを配置してget_site_transient()でネットワーク共有のログイン制限を実装、④アップロードディレクトリのサイト間分離とPHP実行禁止の.htaccess設定、⑤スーパー管理者ログインをメール通知付きで監査ログに記録します。

お気軽にご相談ください

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