2026年5月20日

2026年5月20日

WordPressのフィッシング攻撃からアカウントを守る方法

はじめに

フィッシング攻撃では、攻撃者がWordPress管理者を偽のログインページに誘導してパスワードを盗むか、ソーシャルエンジニアリングで認証情報を取得します。2要素認証(2FA)・異常ログイン検出・強力なパスワードポリシーを組み合わせることで、フィッシングで認証情報が盗まれても不正アクセスを防げます。

症状・原因

  • WordPressのログイン画面を模した偽サイトへの誘導メールが届いた
  • パスワードを変更していないのに「パスワードが変更されました」の通知が届いた
  • 知らない場所・デバイスからのログインがあった
  • フィッシングメールに記載されたリンクをクリックしてしまった

解決手順

ステップ1:フィッシングの被害を確認する

# 最近のログイン履歴を確認(アクティビティログプラグイン使用時)
wp eval "
global \$wpdb;
\$logins = \$wpdb->get_results(\"
    SELECT * FROM {\$wpdb->prefix}activity_log
    WHERE action IN ('login_success', 'login_failed')
    ORDER BY created_at DESC LIMIT 30
\");
foreach (\$logins as \$log) {
    echo \$log->created_at . ' | ' . \$log->ip_address . ' | ' . \$log->message . PHP_EOL;
}
"

# 管理者アカウントの最終ログイン時刻を確認
wp user get 1 --fields=ID,user_login,user_email

# 不審なセッションが存在するか確認
wp eval "
\$users = get_users(['role' => 'administrator']);
foreach (\$users as \$user) {
    \$sessions = get_user_meta(\$user->ID, 'session_tokens', true);
    echo \$user->user_login . ': ' . count((array)\$sessions) . ' sessions' . PHP_EOL;
}
"

# 不審なセッションを即座に全削除
wp eval "
\$users = get_users(['role' => 'administrator']);
foreach (\$users as \$user) {
    \$manager = WP_Session_Tokens::get_instance(\$user->ID);
    \$manager->destroy_all();
    echo 'Sessions cleared for: ' . \$user->user_login . PHP_EOL;
}
"

ステップ2:2要素認証(2FA)を設定する

# 2FA プラグインをインストール
wp plugin install two-factor --activate

# または WP 2FA プラグイン
wp plugin install wp-2fa --activate
// functions.php: 管理者に 2FA を強制する

// ① 管理者が 2FA を設定していない場合にリダイレクト
add_action('admin_init', function(): void {
    if (!current_user_can('manage_options')) return;
    if (defined('DOING_AJAX') && DOING_AJAX) return;

    $user_id = get_current_user_id();

    // Two Factor プラグインの 2FA 設定確認
    if (class_exists('Two_Factor_Core')) {
        $providers = Two_Factor_Core::get_enabled_providers_for_user(get_userdata($user_id));
        if (empty($providers)) {
            // 2FA 設定ページにリダイレクト(設定ページURL は環境によって異なる)
            if (!isset($_GET['page']) || $_GET['page'] !== 'two-factor-user-settings') {
                wp_redirect(admin_url('profile.php#two-factor-options'));
                exit;
            }
        }
    }
});

// ② ログイン成功時に通知メールを送信
add_action('wp_login', function(string $user_login, \WP_User $user): void {
    // 管理者のみ通知
    if (!user_can($user, 'manage_options')) return;

    $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
    $ua = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
    $time = current_time('Y-m-d H:i:s');

    wp_mail(
        $user->user_email,
        '[WordPress] 管理者ログイン通知',
        "以下のアクセスがありました。\n\n"
        . "時刻: {$time}\n"
        . "IP: {$ip}\n"
        . "ブラウザ: {$ua}\n\n"
        . "心当たりがない場合は、すぐにパスワードを変更し、全セッションを無効化してください:\n"
        . admin_url('profile.php')
    );
}, 10, 2);

ステップ3:強力なパスワードポリシーを強制する

// functions.php: パスワードポリシーの強制

// ① パスワード強度の検証
add_action('user_profile_update_errors', function(\WP_Error $errors, bool $update, \WP_User $user): void {
    if (empty($_POST['pass1'])) return;

    $password = $_POST['pass1'];

    // 最低12文字
    if (strlen($password) < 12) {
        $errors->add('weak_password', 'パスワードは12文字以上にしてください。');
    }

    // 大文字・小文字・数字・記号を含む
    if (!preg_match('/[A-Z]/', $password)) {
        $errors->add('weak_password', 'パスワードに大文字を含めてください。');
    }
    if (!preg_match('/[a-z]/', $password)) {
        $errors->add('weak_password', 'パスワードに小文字を含めてください。');
    }
    if (!preg_match('/[0-9]/', $password)) {
        $errors->add('weak_password', 'パスワードに数字を含めてください。');
    }
    if (!preg_match('/[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/', $password)) {
        $errors->add('weak_password', 'パスワードに記号を含めてください。');
    }

    // よく使われるパスワードをブロック
    $common_passwords = ['password123', 'wordpress', 'admin123', 'letmein'];
    if (in_array(strtolower($password), $common_passwords, true)) {
        $errors->add('weak_password', 'このパスワードは使用できません。');
    }
}, 10, 3);

// ② パスワードの有効期限(90日)
add_action('wp_login', function(string $login, \WP_User $user): void {
    $last_changed = get_user_meta($user->ID, 'password_last_changed', true);
    if (!$last_changed) {
        update_user_meta($user->ID, 'password_last_changed', time());
        return;
    }

    $days = (time() - (int)$last_changed) / DAY_IN_SECONDS;
    if ($days > 90 && user_can($user, 'manage_options')) {
        wp_redirect(admin_url('profile.php?password_expired=1'));
        exit;
    }
}, 10, 2);

ステップ4:不審なログインを検出してブロックする

// functions.php: 不審なログインパターンの検出

add_action('wp_login', function(string $login, \WP_User $user): void {
    $ip = $_SERVER['REMOTE_ADDR'] ?? '';
    $user_id = $user->ID;

    // 前回ログインのIPを取得
    $last_ip = get_user_meta($user_id, 'last_login_ip', true);
    $last_country = get_user_meta($user_id, 'last_login_country', true);

    // 現在のIPを保存
    update_user_meta($user_id, 'last_login_ip', $ip);
    update_user_meta($user_id, 'last_login_time', time());

    // 前回と異なるIPからのログインを通知(管理者のみ)
    if ($last_ip && $last_ip !== $ip && user_can($user, 'manage_options')) {
        wp_mail(
            get_option('admin_email'),
            '[警告] 通常と異なるIPからのログイン',
            "ユーザー: {$login}\n"
            . "前回IP: {$last_ip}\n"
            . "今回IP: {$ip}\n"
            . "時刻: " . current_time('Y-m-d H:i:s') . "\n\n"
            . "心当たりがない場合はパスワードを変更してください。"
        );
    }
}, 10, 2);

ステップ5:wp-login.php のURLをカスタマイズして偽サイト誘導を困難にする

// functions.php: ログインURLを変更してフィッシングを困難にする
// ※ WPS Hide Login プラグインを使用する方が簡単

// カスタムログインURL のスラッグ
define('CUSTOM_LOGIN_SLUG', 'my-secure-portal');

add_action('init', function(): void {
    if (!defined('CUSTOM_LOGIN_SLUG')) return;

    // カスタムログインスラッグへのアクセスを wp-login.php にルーティング
    if (isset($_GET[CUSTOM_LOGIN_SLUG])) {
        // 内部的に wp-login.php に渡す
    }

    // wp-login.php への直接アクセスをブロック
    global $pagenow;
    if ($pagenow === 'wp-login.php' && !isset($_GET[CUSTOM_LOGIN_SLUG])) {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            wp_redirect(home_url('/404'));
            exit;
        }
    }
});

// ログインURLをカスタムURLに変更
add_filter('login_url', function(string $url): string {
    return home_url('/' . CUSTOM_LOGIN_SLUG . '/');
}, 10);

注意事項

  • 2要素認証を管理者に強制する前に、必ず自分のアカウントで2FAが正常に機能することを確認してください。設定を誤ると管理画面にアクセスできなくなります
  • ログインURL変更後はWordPressパスワードリセットメール内のURLも変更されます。lostpassword_urlフィルターでリセットURLも合わせて変更する必要があります
  • フィッシングメール内のリンクをクリックした可能性がある場合は、まずwp config shuffle-saltsで全セッションを無効化し、管理者パスワードを変更してから2FAを設定してください

まとめ

フィッシング対策は①WP_Session_Tokens::destroy_all()で不審なセッションを全削除、②wp plugin install two-factorで2FAを設置・管理者に強制、③ログイン成功時にwp_loginフックでメール通知、④パスワードポリシー(12文字以上・大小数字記号)をuser_profile_update_errorsで強制、⑤通常と異なるIPからのログインを検出して管理者に通知します。

お気軽にご相談ください

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