This is an old revision of the document!
入力文字エンコーディングをバリデーションする
PHPの文字列型は文字エンコーディング情報を持たないバイナリ型である。APIや外部システムが入力文字エンコーディングのバリデーションを行っている場合も多いが、文字エンコーディングを利用したインジェクション攻撃のリスクを無くす為に入力文字エンコーディングはバリデーションしなければならない。
外部からの文字列型データの文字エンコーディングは全て正しい文字エンコーディングであることをバリデーションしなければならない。
非適合コード例
<?php $name = filter_var($_GET['name'], FILTER_DEFAULT, [FILTER_FLAG_STRIP_LOW]); ?> Your name is <?php echo htmlspecialchars($name) ?>.
filter_var関数のFILTER_DEFAULTのみでは$nameは$_GET['name']の値となる。FILTER_FLAG_STRIP_LOWでASCII値の32以下の文字が削除される。$nameはASCII値の32以下の文字を含まない文字列となるが、文字エンコーディングはバリデーションされない。
htmlspecialchars関数は文字エンコーディングをバリデーションするが、PHP 5.6以下の場合はデフォルト文字エンコーディングがISO 8859-1となり、INI設定がデフォルトの場合、不正な出力が行われる。
適合コード例
<?php // PHP 5.6未満の場合、internal_encoding設定なども必要 ini_set('default_charset', 'UTF-8'); // 文字列型データの場合、長さのチェックも行うべき if (strlen($_GET['name'] > 100) { throw new Exception('Too long string'); } // 文字エンコーディングチェック if (mb_check_encoding($_GET['name']) === FALSE) { throw new Exception('Invalid encoding'); } $name = filter_var($_GET['name'], FILTER_DEFAULT, [FILTER_FLAG_STRIP_LOW]); // 不要な文字を含む場合のバリデーション if (strlen($name) !== strlen($_GET['name'])) { throw new Exception('Contains invalid chars'); } ?> Your name is <?php echo htmlspecialchars($name) ?>.
PHP 5.6以降の場合、ini_set('default_charset', 'UTF-8')で全ての文字エンコーディングのデフォルト値がUTF-8になる。mb_check_encoding($_GET['name'])でUTF-8エンコーディングであることが確認される。
filterモジュールには文字列バリデーションオプションがない。サニタイズフィルターを適用後、文字列の長さの変化でバリデーションすることが可能である。
例外
$_GET/$_POST/$_COOKIEなどブラウザからの変数は正しい文字エンコーディングであることが保証されない。しかし、データベースなどの場合、保存時に文字エンコーディングがバリデーションされる物もある。この場合、入力文字エンコーディングのバリデーションを省略しても構わない。
リスク評価
文字エンコーディングを利用したインジェクション攻撃が可能となるケースはプログラム内のどこにあるのか判別しづらい。ライブラリや外部システムなどの入れ替えで文字エンコーディング脆弱になる場合もある。
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| IDS05-J | medium | likely | medium | P4 | L3 |
