2016年6月27日月曜日

【備忘録】WordPressのログインパスワードリセットを制限する方法(メールアドレスだけOK)

単に使えないようにする情報は、結構掲載されています。
CSSで非表示にしたり、サーバー(htaccess含む)でリダイレクトしたり、WordPressのパスワード再発行のフック(allow_password_reset)で常に許可しない(false)を返すようにしたりと。

でも条件付きで許可する方法は探せませんでした。ので上記をヒントに、WordPressのコアコードを読んで検証してみました。その結果として、パスワードリセットを電子メールアドレスのみ有効にするという方法を紹介します。本件は、パスワード再発行について(WordPress.org 日本語版フォーラム)のやり取りにてうまくいったので備忘録を込めてここに纏めようと思いました。あとからわかったのですが、二通りのやり方がありましたし、コードももう少し洗練させたほうがいいなぁとも思いましたので。

以下のコードいずれかを「functions.php」 に追加することで、WordPressのパスワードリセットについてユーザー名に電子メールアドレスを使っている人だけ許可、それ以外は許可しないに設定できます。このコードの意味について順をおって説明します。

コード A(functions.php)


パスワードリセットで入力されたユーザー名が電子メールアドレスなら許可。
実際のユーザー名は、電子メールアドレスでなくてもよい
(ユーザー名か電子メールアドレスのいずれかにヒットすればいい)


add_action( 'allow_password_reset', 'disable_password_reset_except_email', 10, 2);

function disable_password_reset_except_email ( $bool, $user_id ) {
  global $_POST;
  $username = sanitize_text_field ( $_POST['user_login'] );
  if(filter_var($username, FILTER_VALIDATE_EMAIL) === false)
return false;

  return  $bool;
}


コード B(functions.php)


パスワードリセットで入力されたユーザー名と登録されたユーザー名のいずれも電子メールアドレスなら許可。

add_action( 'allow_password_reset', 'disable_password_reset_except_email', 10, 2);

function disable_password_reset_except_email ( $bool, $user_id ) {
  global $_POST;


  $user = get_userdata($user_id);

  $login_username = $user->user_login;
  $input_username =  sanitize_text_field ($_POST['user_login']);
  if(filter_var($login_username, FILTER_VALIDATE_EMAIL) === false || 
     filter_var($input_username, FILTER_VALIDATE_EMAIL) === false)
return false;

  return  $bool;
}

allow_password_reset フック


wp-includes/user.php の 2039-2053(WordPress 4.5.3)の抜粋
----

/**
* Filter whether to allow a password to be reset.
*
* @since 2.7.0
*
* @param bool $allow         Whether to allow the password to be reset. Default true.
* @param int  $user_data->ID The ID of the user attempting to reset a password.
*/
$allow = apply_filters( 'allow_password_reset', true, $user->ID );

if ( ! $allow ) {
return new WP_Error( 'no_password_reset', __( 'Password reset is not allowed for this user' ) );
} elseif ( is_wp_error( $allow ) ) {
return $allow;
}
----

ここがパスワードリセットの主要コードになります。
$allow が true でなければ(falseなら)、パスワードリセットが許可されていないということが読み取れます。ここに、apply_filters というアクションフックが用意されています。この関数があれば外部から $allow に入れる値を制御できます。

add_action( 'allow_password_reset', 'disable_password_reset_except_email', 10, 2);

function disable_password_reset_except_email ( $bool, $user_id ) {
  return false;
}

のように、add_action関数を通じて apply_filtersで指定した第一引数「allow_password_reset」をキーワードに、apply_filtersの第二引数である true をどういった値に変更するか disable_password_reset_except_email 関数で決めるという設定です。このadd_actionがなければ、
  • $allow = apply_filters( 'allow_password_reset', true, $user->ID );
で指定された第2引数(true)がデフォルトの値になります。

また、add_actionの 10 は優先順位(デフォルト10)、2(デフォルトは1)は引数です。引数として、trueと$user->IDの二つが入るためです。この2を指定しなければ、
function disable_password_reset_except_email ( $bool, $user_id ) {
の$user_idには値はNULLしか入りません。

ユーザIDからユーザー名を取得する


コードを読めば一目瞭然だと思いますが
  •   $user = get_userdata($user_id);
  •   $login_username = $user->user_login;
のように、get_userdataにIDをわたして、ユーザー全体の情報を取得し、そこからユーザー名(user_login)を抽出するという形が一般的になります。
ここでは特にエスケープ処理(セキュリティ対策)はしていません。これは WordPress標準関数の get_userdataに委ねています。他方
  •   $input_username =  sanitize_text_field ($_POST['user_login']);
はエスケープしています。これはブラウザから直接入力するデータのためです。
取得するフォームの user_login データは、input の textフィールドのため、これを無害化するWordPress標準関数の sanitize_text_field を利用し、変なコードが紛れ込まないようにしています。

あとは PHP標準関数の「filter_var」関数を利用して電子メールアドレスかどうかのチェックをし、false(電子メールアドレスではない)なら、関数の値として falseを返す($allowにfalseがセットされる)ようにしています。この関数は厳密にチェックするようので、docomoなど一部 RFC 5321に違反している(@の左側にドットが2連続していたり、@ の直前のドットがあったり等)場合には、電子メールアドレスとみなされないで注意が必要です。

2016年6月27日 @kimipooh

0 件のコメント:

コメントを投稿