問題はすべて2択で、回答は下の写真のように、赤または青のシールをボードに貼り付ける形になっています。回答がわりとバラけていて、出題者的にはいい感じですね。
以下、出題と解答、解説を記載します。
問題1
以下のPHPスクリプトで、クッキーPHPSESSIDの削除として機能するのはどちら?PHPのバージョンは7.3.6とする。
A) (正解)31人が選択=正答率 83.8%
<?php setcookie('PHPSESSID');
B) 6人が選択
<?php header('Set-Cookie: PHPSESSID=');
解説
setcookie関数の第2引数の省略時の値は '' (空文字列)ですので、A)は setcookie('PHPSESSID', ''); としたのと同じはずです。そして、第2引数が空文字列の場合は、実際にはクッキーの値としてdeletedが指定され、expires属性が過去日時(PHP-7.3.6の場合は、Thu, 01-Jan-1970 00:00:01 GMT)になります。すなわち、setcookie関数の第2引数を省略すると、クッキーの削除として機能します。B) header関数の方は、クッキーの値が空文字列になりますが、クッキーそのものは削除されません。
参考記事: PHPのsetcookie関数で空文字列を設定しようとするとクッキーが削除される
ところが、出題の確認時にmodphpallで確認したところ、古いPHPでは上記の挙動にはならず、以下のレスポンスヘッダが送信されることがわかりました。この動作は、setcookie関数の第2引数を省略した場合のみで、空文字列またはnullを指定した場合はマニュアルどおりの動作となります。
Set-Cookie: PHPSESSID=正確に言えば、PHP 4.1.0~PHP 5.6.13までが上記挙動になります。すべてのPHP 4.0と PHP 5.6.14以降、PHP 7以降では、マニュアルどおりの動作となります。この変更は、恐らく以下のバグレポートに対応したものと思われます。
Bug #67131 setcookie() conditional for empty values not met
プログラミング言語のエッジケースの出題をする場合は特に、実環境での動作確認と、処理系の想定バージョンの明記が重要だなとあらためて感じました。
問題2
以下のPHPスクリプトはどちらが表示される? PHPのバージョンは7.3.6とする。<?php $mail = "a@b@example.jp"; var_dump(filter_var($mail, FILTER_SANITIZE_EMAIL));
A) 31人が選択
bool(false)
B) (正解)6人が選択=正答率 16.2%
string(14) "a@b@example.jp"
解説
この問題は「ひっかけ」でして、実際に多くの方がひっかかりました。マニュアルにあるように、filter_var関数に FILTER_SANITIZE_EMAIL を指定した場合は、英字、数字および !#$%&'*+-=?^_`{|}~@.[] 以外のすべての文字を取り除きます。出題の場合は、除去対象の文字がないので、入力値がそのまま出力されます。
メールアドレスの形式を検証するためには、FILTER_SANITIZE_EMAIL ではなく、FILTER_VALIDATE_EMAIL を使用します。
率直に言って、FILTER_SANITIZE_EMAILのユースケースは思いつきません。要らんでしょ、こんなもの。
問題3(PHP考古学)
PHP-5.2.17にてregister_globals=onの環境で、あらかじめ以下のようにセッション変数が設定されている。$_SESSION['user'] = 'yamada';クエリ文字列 user=tanaka
を指定して以下のスクリプトを実行した場合の表示はどちら?
<?php session_start(); echo $user;A)yamada (正解) 23人が選択=正答率 62.2%
B)tanaka 14人が選択
解説
register_globals=on の環境では、session_start()を実行時にセッション変数の値がグローバル変数として初期設定されます。したがって、$_SESSION['user'] = 'yamada'; が設定されている場合、$user の初期値は 'yamada' になります。仮に、このセッション変数がセットされていない場合、クエリ文字列 user=tanaka の方が使われ、$user の初期値は 'tanaka' になります。セッション変数とクエリ文字列(やPOST変数など)の両方に同じパラメータ名がある場合、セッション変数の方が優先されます。その理由は、セッション変数のグローバル変数へのセットは、起動時ではなく、session_start()の実行時に行われるからです。すなわち、同名のパラメータがあった場合は、セッション変数が上書きします。
したがって、register_globals=on の使用は脆弱性の原因になりやすいことはよく知られていますが、セッション変数をグローバル変数の初期値として用いると、特に脆弱性が混入しやすくなります。
例えば、以下のログインチェックのプログラムについて
session_start(); if (! isset($_SESSION['user']) { die('ログインしてください'); } $user = $_SESSION['user'];これを register_globals=on を想定して「直訳」すると以下のようになります。
session_start(); if (! isset($user)) { die('ログインしてください'); } // $user = $_SESSION['user']; // これは不要となるregister_globals=on の場合、session_start()を実行した時点で、セッション変数 $_SESSION['user'] の値はグローバル変数 $user にセットされるため、「これは不要となる」とコメントした行は不要となります。便利ですね!
しかし、$_SESSION['user'] がない場合、$user=nullが実行されるわけではないので、クエリ文字列等にuser=tanakaがあった場合、$userには 'tanaka' がセットされます。すなわち、パスワードを知らなくても誰にでもなりすましできます。これは重大な問題ですね。
register_globals=on を用いて、上記の脆弱性を修正するには以下のように書くべきですが…
$user = null; // register_globals=onによる初期設定を無効化する session_start(); // $_SESSION['user'] がある場合、$userにその値がセットされる if (! isset($user)) { die('ログインしてください'); }上記は著しく直感に反するプログラムですし、それゆえに初期化漏れが盛大に発生しそうです。なので、register_globalsがPHP 5.4で削除されたのは、必然のことと言えましょう。
[PR]
徳丸が代表を務めるEGセキュアソリューションズ株式会社では、ウェブサイトを堅牢にするための各種セキュリティサービスを提供しています。
0 件のコメント:
コメントを投稿