【やさしく解説】PHP8上級試験の模擬問題(17)

PHP
スポンサーリンク

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

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

今回は(クロスサイトスクリプティング)対策に関する問題です。​htmlspecialchars() や htmlentities() 関数の適切な使い方、エスケープの挙動、配列入力時の注意点など、セキュリティ上の重要なポイントが問われています。実務でも頻繁に遭遇するテーマですので、しっかり押さえていきましょう。

PRIME STUDY認定模擬問題のリンクはこちらです → https://study.prime-strategy.co.jp/ 

問題文

XSS (クロスサイトスクリプティング) に関する説明の中で、誤っているものを1つ選びなさい。
なお、すべてのコードの先頭には下記のコードが書かれているものとする。

declare(strict_types=1);
error_reporting(-1);
下記はマニュアルから一部引用した内容である。

htmlspecialchars ( string $string , int $flags = ENT_COMPAT , string|null $encoding = null , bool $double_encode = true ) : string
htmlentities ( string $string , int $flags = ENT_COMPAT , string|null $encoding = null , bool $double_encode = true ) : string

第二引数 (flags) の定数
ENT_COMPAT ダブルクオートは変換しますがシングルクオートは変換しません。
ENT_QUOTES シングルクオートとダブルクオートを共に変換します。
ENT_NOQUOTES シングルクオートとダブルクオートは共に変換されません。

(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が正解となります。

他の問題へ

コメント

タイトルとURLをコピーしました