2026年11月13日
2026年11月13日
WordPressのユーザー登録フォームをカスタマイズしてセキュリティを強化する方法
はじめに
WordPressのデフォルトユーザー登録フォームはシンプルすぎて、追加情報の収集やスパムアカウント対策が困難です。register_formアクションでカスタムフィールドを追加し、registration_errorsフィルターで厳密なバリデーション、さらに招待コードによるアクセス制限まで実装してサイトのセキュリティを大幅に向上させましょう。
症状・原因
- スパムボットによる大量の偽アカウント登録が発生している
- 登録時に追加情報(会社名・電話番号など)を収集できない
- 誰でも登録できてしまい、会員限定サービスが実現できない
- 登録後のウェルカムメールがデフォルトで味気なく、ブランドイメージと合わない
- 登録ユーザーのロールを自動で適切に設定したい
解決手順
ステップ1:register_formアクションでカスタムフィールドを追加
<?php
/**
* ユーザー登録フォームにカスタムフィールドを追加
* wp-login.php?action=register のフォームに追加される
*/
function add_registration_custom_fields() {
// セッションが始まっていない場合は開始
if ( ! session_id() ) {
session_start();
}
// バリデーションエラー後の値の復元
$first_name = $_SESSION['reg_first_name'] ?? '';
$last_name = $_SESSION['reg_last_name'] ?? '';
$company = $_SESSION['reg_company'] ?? '';
$invite_code = $_SESSION['reg_invite_code'] ?? '';
// セッションをクリア
unset( $_SESSION['reg_first_name'], $_SESSION['reg_last_name'],
$_SESSION['reg_company'], $_SESSION['reg_invite_code'] );
?>
<p>
<label for="first_name"><?php _e( '姓', 'your-theme' ); ?><br/>
<input type="text" name="first_name" id="first_name"
class="input" value="<?php echo esc_attr( $last_name ); ?>"
size="25" required />
</label>
</p>
<p>
<label for="last_name"><?php _e( '名', 'your-theme' ); ?><br/>
<input type="text" name="last_name" id="last_name"
class="input" value="<?php echo esc_attr( $first_name ); ?>"
size="25" required />
</label>
</p>
<p>
<label for="company"><?php _e( '会社名・組織名(任意)', 'your-theme' ); ?><br/>
<input type="text" name="company" id="company"
class="input" value="<?php echo esc_attr( $company ); ?>"
size="25" />
</label>
</p>
<p>
<label for="invite_code">
<?php _e( '招待コード', 'your-theme' ); ?>
<span class="required">*</span><br/>
<input type="text" name="invite_code" id="invite_code"
class="input" value="<?php echo esc_attr( $invite_code ); ?>"
size="25" placeholder="招待コードを入力してください"
required />
</label>
<span class="description"><?php _e( '招待コードをお持ちでない方はお問い合わせください。', 'your-theme' ); ?></span>
</p>
<!-- 利用規約への同意チェックボックス -->
<p>
<label>
<input type="checkbox" name="terms_agreed" value="1" required />
<a href="/terms/" target="_blank">利用規約</a>および
<a href="/privacy/" target="_blank">プライバシーポリシー</a>に同意します
</label>
</p>
<?php
}
add_action( 'register_form', 'add_registration_custom_fields' );
?>
ステップ2:registration_errorsフィルターでバリデーション
<?php
/**
* 登録フォームのカスタムバリデーション
* エラーがあれば WP_Error オブジェクトに追加して返す
*/
function validate_registration_custom_fields( $errors, $sanitized_user_login, $user_email ) {
// --- 名前の検証 ---
if ( empty( $_POST['first_name'] ) || empty( $_POST['last_name'] ) ) {
$errors->add( 'name_required', '<strong>エラー</strong>: 姓名を入力してください。' );
}
// --- 招待コードの検証 ---
$invite_code = sanitize_text_field( $_POST['invite_code'] ?? '' );
if ( empty( $invite_code ) ) {
$errors->add( 'invite_code_required', '<strong>エラー</strong>: 招待コードを入力してください。' );
} else {
// 有効な招待コードのリスト(実際はDBやオプションから取得)
$valid_codes = get_option( 'valid_invite_codes', array() );
if ( ! in_array( $invite_code, $valid_codes, true ) ) {
$errors->add( 'invite_code_invalid', '<strong>エラー</strong>: 招待コードが無効です。' );
}
}
// --- 利用規約への同意確認 ---
if ( empty( $_POST['terms_agreed'] ) ) {
$errors->add( 'terms_not_agreed', '<strong>エラー</strong>: 利用規約への同意が必要です。' );
}
// --- メールアドレスのドメイン制限(例:会社ドメインのみ許可) ---
$allowed_domains = get_option( 'registration_allowed_domains', array() );
if ( ! empty( $allowed_domains ) ) {
$email_domain = substr( $user_email, strpos( $user_email, '@' ) + 1 );
if ( ! in_array( $email_domain, $allowed_domains, true ) ) {
$errors->add(
'email_domain_not_allowed',
'<strong>エラー</strong>: このメールアドレスのドメインでは登録できません。'
);
}
}
// バリデーション失敗時にセッションに値を保存(フォームに値を戻すため)
if ( $errors->get_error_codes() ) {
if ( ! session_id() ) session_start();
$_SESSION['reg_first_name'] = sanitize_text_field( $_POST['first_name'] ?? '' );
$_SESSION['reg_last_name'] = sanitize_text_field( $_POST['last_name'] ?? '' );
$_SESSION['reg_company'] = sanitize_text_field( $_POST['company'] ?? '' );
$_SESSION['reg_invite_code'] = $invite_code;
}
return $errors;
}
add_filter( 'registration_errors', 'validate_registration_custom_fields', 10, 3 );
?>
ステップ3:user_registerアクションで登録後のメタ保存
<?php
/**
* ユーザー登録完了後にカスタムメタを保存
* user_register アクションはユーザーID作成後に実行される
*/
function save_registration_custom_meta( $user_id ) {
// --- カスタムフィールドの保存 ---
if ( ! empty( $_POST['first_name'] ) ) {
update_user_meta( $user_id, 'first_name', sanitize_text_field( $_POST['first_name'] ) );
}
if ( ! empty( $_POST['last_name'] ) ) {
update_user_meta( $user_id, 'last_name', sanitize_text_field( $_POST['last_name'] ) );
}
if ( ! empty( $_POST['company'] ) ) {
update_user_meta( $user_id, 'company', sanitize_text_field( $_POST['company'] ) );
}
// --- 招待コードの使用済みマーク ---
$invite_code = sanitize_text_field( $_POST['invite_code'] ?? '' );
$valid_codes = get_option( 'valid_invite_codes', array() );
if ( in_array( $invite_code, $valid_codes, true ) ) {
// 使用済みコードを削除(一回限り有効)
$valid_codes = array_diff( $valid_codes, array( $invite_code ) );
update_option( 'valid_invite_codes', array_values( $valid_codes ) );
// 使用した招待コードをユーザーメタに記録
update_user_meta( $user_id, 'used_invite_code', $invite_code );
}
// --- 登録日時の記録 ---
update_user_meta( $user_id, 'registration_date', current_time( 'mysql' ) );
// --- ユーザーロールの設定(デフォルトの subscriber から変更) ---
$user = new WP_User( $user_id );
$user->set_role( 'contributor' ); // または 'subscriber', 'author' など
// --- 利用規約同意の記録 ---
update_user_meta( $user_id, 'terms_agreed_date', current_time( 'mysql' ) );
update_user_meta( $user_id, 'terms_agreed_version', '1.0' );
}
add_action( 'user_register', 'save_registration_custom_meta' );
?>
ステップ4:登録制限とオプション管理
<?php
/**
* 登録を制限する:一般公開登録を無効化し
* 特定の条件(招待コード)がある場合のみ許可
*/
// --- 管理画面の設定オプションに招待コード管理UIを追加 ---
function add_invite_code_settings_page() {
add_options_page(
'招待コード管理', // ページタイトル
'招待コード', // メニューラベル
'manage_options', // 必要な権限
'invite-codes', // スラッグ
'render_invite_code_page' // コールバック関数
);
}
add_action( 'admin_menu', 'add_invite_code_settings_page' );
function render_invite_code_page() {
if ( ! current_user_can( 'manage_options' ) ) return;
// 新しいコードの追加処理
if ( isset( $_POST['add_invite_code'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'manage_invite_codes' ) ) {
$new_code = sanitize_text_field( $_POST['new_invite_code'] );
$valid_codes = get_option( 'valid_invite_codes', array() );
if ( ! empty( $new_code ) && ! in_array( $new_code, $valid_codes, true ) ) {
$valid_codes[] = $new_code;
update_option( 'valid_invite_codes', $valid_codes );
echo '<div class="notice notice-success"><p>招待コードを追加しました。</p></div>';
}
}
// コードの削除処理
if ( isset( $_GET['delete_code'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'delete_invite_code' ) ) {
$code_to_delete = sanitize_text_field( $_GET['delete_code'] );
$valid_codes = get_option( 'valid_invite_codes', array() );
$valid_codes = array_diff( $valid_codes, array( $code_to_delete ) );
update_option( 'valid_invite_codes', array_values( $valid_codes ) );
}
$valid_codes = get_option( 'valid_invite_codes', array() );
?>
<div class="wrap">
<h1>招待コード管理</h1>
<form method="post">
<?php wp_nonce_field( 'manage_invite_codes' ); ?>
<table class="form-table">
<tr>
<th>新しい招待コードを追加</th>
<td>
<input type="text" name="new_invite_code" class="regular-text"
placeholder="例:INVITE2024" />
<button type="submit" name="add_invite_code" class="button button-primary">追加</button>
</td>
</tr>
</table>
</form>
<h2>有効な招待コード一覧(<?php echo count( $valid_codes ); ?>件)</h2>
<table class="wp-list-table widefat fixed">
<thead><tr><th>招待コード</th><th>操作</th></tr></thead>
<tbody>
<?php foreach ( $valid_codes as $code ) : ?>
<tr>
<td><code><?php echo esc_html( $code ); ?></code></td>
<td>
<a href="<?php echo wp_nonce_url(
add_query_arg( 'delete_code', urlencode( $code ) ),
'delete_invite_code'
); ?>" class="button button-small" onclick="return confirm('削除しますか?')">削除</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
}
?>
ステップ5:カスタムウェルカムメールを送信
<?php
/**
* デフォルトの登録通知メールを置き換えるカスタムウェルカムメール
* wp_new_user_notification_email フィルターで内容を変更
*/
// --- ユーザー向けウェルカムメールのカスタマイズ ---
function custom_new_user_notification_email( $wp_new_user_notification_email, $user, $blogname ) {
$user_login = stripslashes( $user->user_login );
$user_email = stripslashes( $user->user_email );
$wp_new_user_notification_email['subject'] = sprintf(
'[%s] ご登録ありがとうございます!', $blogname
);
$message = sprintf( '%s 様', $user->display_name ?: $user_login ) . "\r\n\r\n";
$message .= sprintf( '%s へのご登録ありがとうございます。', $blogname ) . "\r\n\r\n";
$message .= "アカウント情報は以下の通りです:\r\n";
$message .= sprintf( 'ユーザー名: %s', $user_login ) . "\r\n";
$message .= sprintf( 'メールアドレス: %s', $user_email ) . "\r\n\r\n";
$message .= "パスワードは登録時に設定したものをご利用ください。\r\n";
$message .= "パスワードを忘れた場合:\r\n";
$message .= wp_lostpassword_url() . "\r\n\r\n";
$message .= "ログイン:\r\n";
$message .= wp_login_url() . "\r\n\r\n";
$message .= "ご不明な点はお気軽にお問い合わせください。\r\n\r\n";
$message .= sprintf( '%s チーム一同', $blogname );
$wp_new_user_notification_email['message'] = $message;
$wp_new_user_notification_email['headers'] = array(
'Content-Type: text/plain; charset=UTF-8',
sprintf( 'From: %s <%s>', $blogname, get_option( 'admin_email' ) ),
);
return $wp_new_user_notification_email;
}
add_filter( 'wp_new_user_notification_email', 'custom_new_user_notification_email', 10, 3 );
// --- 管理者向け通知を無効化(管理者に送られるデフォルトメールを止める) ---
function disable_admin_new_user_notification( $wp_new_user_notification_email_admin, $user, $blogname ) {
// 空のメールを返して通知を無効化
$wp_new_user_notification_email_admin['to'] = ''; // 送信先を空にして実質無効化
return $wp_new_user_notification_email_admin;
}
// 管理者通知を無効にしたい場合のみ有効化
// add_filter( 'wp_new_user_notification_email_admin', 'disable_admin_new_user_notification', 10, 3 );
?>
注意事項
- セッション使用:
register_formでセッションを使う場合はsession_start()が早い段階(initフックなど)で呼ばれるようにしてください - スパムボット対策: 招待コードに加えてhoneypotフィールドやCloudflareTurnstileの追加も推奨します
- メールの文字化け:
wp_mail()で日本語メールを送る際はContent-TypeヘッダーにUTF-8を指定し、メールサーバーの設定も確認してください - ユーザーロール:
user_registerアクションでロールを設定する際はWordPressデフォルトのロール設定より後に実行されます
まとめ
register_formでカスタムフィールドを追加、registration_errorsでバリデーション、user_registerでメタ保存、招待コード管理UIで登録制限、wp_new_user_notification_emailフィルターでウェルカムメールをカスタマイズすることで、安全で使いやすい登録フローを構築できます。関連記事:WordPressの404ページをカスタマイズして直帰率を下げる方法