
PHP8 上級/準上級試験の認定模擬問題として、プライム・ストラテジー様のPRIME STUDYが認定されているようです。ただ、この模擬問題には解説がありませんので解説をまとめていこうと思います。シリーズ第17回目です。

PHP初級レベルから脱却して中級/上級レベルにいきたい人にピッタリの内容ですね。

今回は(クロスサイトスクリプティング)対策に関する問題です。htmlspecialchars() や htmlentities() 関数の適切な使い方、エスケープの挙動、配列入力時の注意点など、セキュリティ上の重要なポイントが問われています。実務でも頻繁に遭遇するテーマですので、しっかり押さえていきましょう。
PRIME STUDY認定模擬問題のリンクはこちらです → https://study.prime-strategy.co.jp/
問題文
XSS (クロスサイトスクリプティング) に関する説明の中で、誤っているものを1つ選びなさい。
なお、すべてのコードの先頭には下記のコードが書かれているものとする。
declare(strict_types=1);
error_reporting(-1);
下記はマニュアルから一部引用した内容である。
(1)
HTML の動的な生成において、実装に問題があるとXSS 脆弱性が発生する。
そのためには、適切な引数で htmlspecialchars() 関数、または htmlentities() 関数を使う必要がある。
そのため、以下のコード
$input = "alert('test');";
$e_input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
echo $e_input;
を実行すると
<script>alert('test');</script>
となり、必要なエスケープが全てなされているので、XSSを防ぐ事ができる。
(2)
以下のコード
$e_test = htmlspecialchars(filter_input(INPUT_GET, 'test'), ENT_QUOTES, 'UTF-8');
echo "{$e_test}
";
によって、適切に入力がエスケープされるため XSS を防ぐ事ができる。
ただし、もしパラメタ test に配列が入ってきている場合、またはパラメタ test が存在しない場合は、filter_input() 関数の戻り値の仕様が成功した場合は要求された変数の値、フィルタリングに失敗した場合に false、 あるいは変数 var_name が設定されていない場合に null を返します。
であるため、結果は次のとおりとなる。
Fatal error: Uncaught TypeError: htmlspecialchars(): Argument #1 ($string) must be of type string, null given in …
(3)
以下のコード
$val = $_GET['test'] ?? '';
if ( is_string($val) ) {
$ret = htmlentities($val, ENT_QUOTES, 'UTF-8');
echo "{$ret}
";
} else {
foreach($val as $v) {
echo "{$v}
";
}
}
をブラウザ経由で実行すると、test に文字列が入ってきても配列が入ってきても、適切に XSS 対策を行う事ができる。
(4)
$_GET[‘test’] に文字列で任意のユーザ入力が入っている (配列ではない) とした時に、以下のコード
$ret = htmlspecialchars($_GET['test'], ENT_COMPAT);
echo "{$ret}
";
をブラウザ経由で実行すると、シングルクオートが変換されないため、XSS が発生する可能性がある。この実装は適切ではない。
解説
選択肢1の解説
正しいです。
htmlspecialchars() は XSS を防ぐために有効で、書かれているコードも正しいです。
選択肢2の解説
正しいです。
htmlspecialchars() は入力文字列を適切にエスケープしますが、filter_input() の戻り値が null である場合、htmlspecialchars() に null を渡すとエラーになります。このエラーは、htmlspecialchars() が string 型の引数を要求するためです。
選択肢3の解説
誤りです。
順を追ってみていきましょう。
$val = $_GET['test'] ?? '';
ここで、test パラメータがあればその値を $val に代入し、なければ空文字にします。
if ( is_string($val) ) {
$ret = htmlentities($val, ENT_QUOTES, 'UTF-8');
echo "{$ret}\n";
}
ここでは、$val が「文字列」であれば、htmlentities 関数でエスケープ(無害化)して表示しています。ここまではきちんと対策できてそうですね。
else {
foreach($val as $v) {
echo "{$v}\n";
}
}
ここに問題がありそうです。$val が配列だった場合、$v をそのまま echo しています。
配列の中身までエスケープするには以下のようにする必要があります。
else {
foreach($val as $v) {
$safe_v = htmlentities($v, ENT_QUOTES, 'UTF-8');
echo "{$safe_v}\n";
}
}
選択肢4の解説
正しいです。
htmlspecialchars() を ENT_COMPAT で使用すると、シングルクオートがエスケープされません。したがって、シングルクオートを使った JavaScript のコードが実行される可能性があり、XSS のリスクがあります。ENT_QUOTES を使用するべきです。
正解選択肢
以上より選択肢3が正解となります。
他の問題へ
模擬問題(1)インストール
模擬問題(2)変数の型
模擬問題(3)クラス
模擬問題(4)メソッドその1
模擬問題(5)メソッドその2
模擬問題(6)リファレンス
模擬問題(7)名前空間
模擬問題(8)エラー
模擬問題(9)定義済みのインターフェイスとクラスおよび SPL インターフェイス
模擬問題(10)SPL
模擬問題(11)定義済の変数
模擬問題(12)PHP7.0.xからPHP7.1.xへの移行
模擬問題(13)可変変数
模擬問題(14)PHP7.3.xからPHP7.4.xへの移行
模擬問題(15)PHP7.4.xからPHP8.0.xへの移行
模擬問題(16)制御構造
模擬問題(17)XSS(クロスサイトスクリプティング)
模擬問題(18)ファイルアップロード
模擬問題(19)推測困難なトークン
模擬問題(20)セッション
模擬問題(21)PHP のメモリ消費
模擬問題(22)関数その1
模擬問題(23)関数その2
模擬問題(24)関数その3
模擬問題(25)関数その4
模擬問題(26)関数その5
模擬問題(27)関数その6
模擬問題(28)関数その7
模擬問題(29)関数その8
模擬問題(30)関数その9
コメント