コードベースで二段階認証(2FA)を導入する方法

WordPressのログインセキュリティを高めたい場合、二段階認証(2FA)は必須です。この記事ではプラグインを使わず、PHPと少量のJavaScriptで自前実装する方法を解説します。

1 事前準備

まずはWordPressテーマの functions.php にコードを追加できる状態にしてください。子テーマを利用している場合は functions.php に追加します。

2 function.phpにコードを追加(PHP側で2FAの生成と検証)

ここでは、ログイン時にワンタイムパスワード(OTP)を生成し、ユーザーのメールに送信して認証させる簡易2FAを例にします。

ユーザー名だけでなく、登録メールアドレスでもOTP送信可能です。Ajax送信時に入力欄にユーザー名またはメールアドレスを入力してください。


//-------------------------------------------
// 安全な二段階認証(2FA) - Ajax対応版(複数ユーザー対応)
//-------------------------------------------

// セッション開始
add_action('init', function() {
    if (!session_id()) session_start();
});

// OTP生成関数
function generate_otp() {
    return random_int(100000, 999999); // より安全なランダム生成
}

// ログインフォームにOTP欄と送信ボタンを追加
add_action('login_form', function() {
    ?>
    <p>
        <label for="otp">ワンタイムパスワード(メールで送信)</label>
        <input type="text" name="otp" id="otp" class="input" value="" size="20">
    </p>
    <p>
        <button type="button" id="send-otp" class="button">OTPをメールで送信</button>
        <span id="otp-message" style="margin-left:10px;"></span>
    </p>

    <script>
    document.addEventListener('DOMContentLoaded', function() {
        var btn = document.getElementById('send-otp');
        btn.addEventListener('click', function() {
            var input = document.getElementById('user_login').value;
            if (!input) {
                alert('ユーザー名またはメールアドレスを入力してください');
                return;
            }

            var data = new FormData();
            data.append('action', 'send_otp');
            data.append('input', input);

            fetch('<?php echo admin_url("admin-ajax.php"); ?>', {
                method: 'POST',
                body: data,
                credentials: 'same-origin'
            })
            .then(res => res.json())
            .then(res => {
                var msg = document.getElementById('otp-message');
                if(res.success){
                    msg.style.color = 'green';
                    msg.textContent = res.data;
                } else {
                    msg.style.color = 'red';
                    msg.textContent = res.data || res.message;
                }
            });
        });
    });
    </script>
    <?php
});

// AjaxでOTP送信(複数ユーザー対応)
add_action('wp_ajax_nopriv_send_otp', 'send_otp_ajax');
function send_otp_ajax() {
    if (!empty($_POST['input'])) {
        $input = sanitize_text_field($_POST['input']);

        // ユーザー名またはメールアドレスで検索
        $user = get_user_by('login', $input);
        if (!$user) {
            $user = get_user_by('email', $input);
        }

        if ($user) {
            $otp = rand(100000, 999999);
            $_SESSION['otp_code'] = $otp;
            wp_mail($user->user_email, 'ワンタイムパスワード', 'あなたのOTPコード: ' . $otp);
            wp_send_json_success('OTPを送信しました。メールを確認してください。');
        } else {
            wp_send_json_error('ユーザーが見つかりません。');
        }
    } else {
        wp_send_json_error('ユーザー名またはメールアドレスを入力してください。');
    }
}

// 認証時にOTPチェック
add_filter('authenticate', function($user, $username, $password) {
    if (is_wp_error($user)) return $user;

    if (!isset($_SESSION['otp_code'])) {
        return new WP_Error('otp_not_sent', '<strong>ERROR</strong>: OTPを送信してください。');
    }

    if (empty($_POST['otp'])) {
        return new WP_Error('otp_empty', '<strong>ERROR</strong>: OTPを入力してください。');
    }

    if ($_POST['otp'] != $_SESSION['otp_code']) {
        return new WP_Error('otp_wrong', '<strong>ERROR</strong>: ワンタイムパスワードが間違っています。');
    }

    unset($_SESSION['otp_code']); // OTP破棄
    return $user;
}, 30, 3);

※注意:本実装では、ログインフォームの「ユーザー名」欄に登録メールアドレスを入力してもOTP送信できます。

3 JavaScriptにコードを追加(送信ボタン設置)

ファイル名:login-2fa.js
保存先:wp-content/themes/child-theme/js/login-2fa.js


//-------------------------------------------
// 安全な二段階認証(2FA) - Ajax対応版(複数ユーザー対応)
//-------------------------------------------
<p>
    <button type="button" id="send-otp" class="button">OTPをメールで送信</button>
    <span id="otp-message"></span>
</p>

<!-- OTP送信ボタン処理 -->
<script>
document.addEventListener('DOMContentLoaded', function() {
    document.getElementById('send-otp').addEventListener('click', function() {
        var username = document.getElementById('user_login').value;
        if (!username) {
            alert('ユーザー名を入力してください');
            return;
        }

        var data = new FormData();
        data.append('action', 'send_otp');
        data.append('username', username);

        fetch(admin_url + 'admin-ajax.php', { // PHPタグは使えないので、enqueue時にwp_localize_scriptで変数渡し
            method: 'POST',
            body: data,
            credentials: 'same-origin'
        })
        .then(res => res.json())
        .then(res => {
            document.getElementById('otp-message').textContent = res.data || res.message;
        });
    });
});
</script>

3.1 ディレクトリ構造

  • child-theme/
    • archive.php
    • category.php
    • functions.php
    • footer.php
    • header.php
    • style.css
    • js/
      • login-2fa.js

4 完成イメージ

二段階認証のログイン画面

二段階認証のコード

5 運用上の注意

  • メール送信の遅延によるログイン失敗を防ぐため、SMTP設定を推奨。
  • 本例は簡易実装のため、スマホアプリでのTOTPやGoogle Authenticator連携の方がより安全。
  • ログインページのカスタマイズにより、セキュリティを高めつつUXも向上可能。

以上で、プラグインなしでWordPressに簡易二段階認証を導入する方法の解説でした。