====== 文字列を正規化する ======
文字列の正規化は大別して2種類ある
* Unicode文字列の正規化
* 日本語表記の正規化
PHPの場合、特に理由がない場合、UTF-8エンコーディングを使う方が良い。
Unicodeには文字の表記方法が複数ある。例えば、「がぎぐげご」などの濁音は「か」と「゙」が分離しているNFD表記と一緒になっているNFC表記がある。これらの表記方法が統一されない場合、同一文字であっても異るバイト列になる。これを統一するのが正規化である。特に問題になるのはWindows/UNIX系OSとmacOSのファイル名である。Windows/UNIX系OSはNFC、macOSはNFDを利用している。Windows/UNIX系OSはファイル名を積極的にNFCに変換(正規化)しないため、maxOSのファイル名を正規化なしに利用すると「見た目は同じのファイルが複数存在する」状態になる。
日本語の場合、半角文字と全角文字、ひらがなとカタカナがある。入力仕様として「全角英数字」を受け入れ「半角英数字に」に変換する場合がある。
これらの変換(正規化)処理が必要な場合、入力バリデーション処理の前に行わなければならない。順序が逆になった場合、脆弱性の原因となる。
===== 非適合コード例 =====
**非適合コード**
if (strstr($_POST['filename'], '/')) {
throw new Exception('Invalid filename');
}
$filename = mb_convert_kana($_POST['filename'], 'HVan');
変換後の$filenameは、mb_convert_kana()の変換により'/'が'/'に変換されてstrstr()で確認した文字を含む可能性がある。
===== 適合コード例 =====
**適合コード**
$filename = mb_convert_kana($_POST['filename'], 'HVan');
if (strstr($_POST['filename'], '/')) {
throw new Exception('Invalid filename');
}
文字列の正規化後(変換後)にstrstr()で確認しているので、意図した通りに動作する。
**注意:この確認方法はブラックリスト方式で脆弱である。パス名に含むことができない文字はファイルシステム依存する。本来は安全な文字のみ含むようバリデーションしなければならない。**
このコード例では適切なバリデーションコードの記述を省略している。
===== 例外 =====
正規化とデコードは似ているが異る処理である。正規化の場合、文字列に対して複数回正規化(変換)を行っても、基本的には、結果は変わらない。デコード(ある表現形式のデータにエンコードされたデータを復元する処理)は複数回適用すると、異る結果となる場合がある。
既に変換(デコード)済みのデータに対して、再び変換処理を行ってはならない。複数回の変換処理が脆弱性の原因となる場合がある。
PHPの場合、$_GET/$_POSTなどに保存されるデータは文字列にデコード(変換)済みである。このため複数回のデコード(変換)処理を誤って行う可能性は少ない。
===== リスク評価 =====
[ルール/推奨事項のリスク評価]
[評価例 - 英語表記に統一]
^ Rule ^ Severity ^ Likelihood ^ Remediation Cost ^ Priority ^ Level ^
| IDS05-J | medium | unlikely | medium | P4 | L3 |
===== 関連ガイドライン =====
===== 参考文献 =====