• wp_safe_redirect()は移動先hostを検証し、呼出し後に処理を終了する

wp_safe_redirect()は移動先hostを検証し、呼出し後に処理を終了する

公開日: 執筆:佐藤

ログイン後の戻り先など、要求から受け取ったURLへ移動させる場合は、wp_redirect()へそのまま渡さず、wp_safe_redirect()で移動先のhostを検証します。関数を呼んだだけではPHPの処理は終わらないため、直後にexit;を置きます。

wp_safe_redirect()には戻り値がありません。成功したかをifで判定してから終了する書き方ではなく、転送を指示し、その場で処理を終える書き方にします。

wp_safe_redirect()はURLを整えてからhostを検証する

WordPress 3.8のpluggable.phpでは、wp_safe_redirect()が次の順で処理します。

  1. wp_sanitize_redirect()でリダイレクト用のURLを整える
  2. wp_validate_redirect()でschemeとhostを検証する
  3. 検証後のURLをwp_redirect()へ渡す

wp_validate_redirect()が認めるschemeはhttpとhttpsです。絶対URLにhostがある場合は、サイトのhome_url()と同じhostか、allowed_redirect_hostsフィルターで許可されたhostかを確認します。相対URLにはhostがないため、サイト内の移動先として使えます。

URLが壊れている場合や、絶対URLのhostが許可されていない場合、wp_safe_redirect()はadmin_url()を代わりの移動先にします。入力されたURLを無条件に採用する処理ではありません。

呼出しとexitを一組にする

説明用に、管理画面の処理後にredirect_toへ戻す場合を考えます。

$redirect_to = isset( $_GET['redirect_to'] )
	? wp_unslash( $_GET['redirect_to'] )
	: admin_url();

wp_safe_redirect( $redirect_to );
exit;

wp_safe_redirect()はLocationヘッダーを送るwp_redirect()を呼びますが、その後のPHPコードを止めません。exit;がなければ、転送を指示したあとも、保存、削除、画面出力などの処理が続く可能性があります。

転送は処理の結末に置きます。入力検査、権限確認、保存結果の判定を終えてから移動先を決め、wp_safe_redirect()とexit;で応答を終えます。

戻り値をifで判定しない

次の書き方は使いません。

if ( wp_safe_redirect( $redirect_to ) ) {
	exit;
}

WordPress 3.8のwp_safe_redirect()は何も返しません。そのため、条件式は真にならず、exit;まで進みません。一方、関数の内部ではすでにwp_redirect()が呼ばれているため、転送ヘッダーを送った状態で後続処理だけが続きます。

wp_redirect()は、移動先があればtrue、なければfalseを返します。ただし、こちらも処理を終了しません。二つの関数について、「転送を指示すること」と「プログラムを終了すること」を分けて理解します。

safeは移動先の権限まで確認する意味ではない

wp_safe_redirect()が検証するのは、リダイレクトに使うURLの形、scheme、hostです。現在の利用者が保存や削除を実行できるか、移動先のページを閲覧できるか、移動先が実在するかまでは判定しません。

管理画面の処理では、先に利用者の権限と操作要求を確認します。その後、処理結果に合うサイト内URLを作り、最後に転送します。関数名のsafeを、操作全体の安全性を保証する言葉として広げないことが重要です。

外部hostへの移動が業務上必要なら、allowed_redirect_hostsへ固定したhostを追加できます。しかし、要求から受け取ったhostをそのまま許可一覧へ加えると、検証の意味がなくなります。許可するhostは、コードまたは設定の管理者が先に決めます。

同じhostと異なるhostを分けて試す

実装後は、少なくとも三つの移動先で結果を確認します。

入力する移動先 確認する結果
サイト内の相対URL 指定したページへ移動する
home_url()と同じhostの絶対URL 指定したページへ移動する
許可していないhostの絶対URL admin_url()へ移動する

さらに、転送後に置いたテスト用の記録や保存処理が実行されないことを確かめます。ブラウザーが移動したという表示だけでは、PHPの後続処理が止まったかは判断できません。

最初の一手は、リダイレクトを行う一箇所を選び、移動先の作り方、権限確認、wp_safe_redirect()、exit;の順をコード上で追うことです。外部hostを許可する必要がなければ、許可一覧を広げず、サイト内の移動だけに使います。

戻る