This is an old revision of the document!
Table of Contents
外部入力のデータを無闇にキャストしない
外部入力の整数データは不用意にキャストしてはならない。外部データの整数データが利用するPHPの整数型の範囲内であることが確実である場合にのみキャストが行える。これは特に32ビットOSでのPHP動作を保証する場合に重要である。
- 64ビットOSのPHPの整数型は符号付き64ビット整数(PHP 7.0未満の場合、64ビットWindows OSの場合も整数は32ビット整数)
- 32ビットOSのPHPの整数型は符号付き32ビット整数
64ビット整数と32ビット整数では表現可能なデータ範囲が大きく異る。このため32ビット整数環境のPHPでは簡単にデータの完全性が損なわれる。
大きな整数を持つ外部入力の代表例はデータベースシステムだが、データベースシステムの出力はブラウザへの出力として利用され、再びブラウザからの入力としてPHPに戻って来る場合も多い。
例:
- 符号無し64bit整数型のレコードID
- 任意精度型のカラム
データの正確性/完全性を重視するソフトウェアの場合、整数/数値を文字列として渡す場合が多く、Webブラウザからの入力は全て文字列として渡される。
非適合コード例
非適合コードはキャストオペレータを利用し、PHPの整数型の表現範囲を超える外部入力をキャストする。PHPのキャストオペレータは整数オーバーフローを検出しない。キャストによりデータが丸められた場合、意図しない操作となる。
データベースのレコードIDには64ビット整数が用いられることが多い。32ビット整数の環境の場合、約20億までの整数しか表現できず、比較的簡単に意図しない操作となる。64ビット整数の環境の場合も、データベース側で符号無し64ビット整数が利用された場合はデータベース側で取り扱えるID数の半分しか正常に操作できない。レコードIDに64ビット整数のハッシュ値などが利用されている場合は顕著な問題となる。
<?php $db = pg_connect('host=localhost'); if (!$db) { throw new Exception('Cannot connect to database'); } // $id may be set to PHP_INT_MAX/MIN value when $_GET['id'] exceeds PHP integer range. $id = (int)$_GET['id']; $sql = 'SELECT * FROM sample WHERE num = $1'; $ret = pg_query_params($sql, [$id]); if ($ret === FALSE) { throw new Exception('Query failed'); } ?>
適合コード例 (strpsn)
符号無し整数であることを確認するには文字列が0から9の整数のみを含むことを確認すればよい。
<?php $db = pg_connect('host=localhost'); if (!$db) { throw new Exception('Cannot connect to database'); } // There should be arbitrary length limit validation if (strspn(strlen($_GET['id']) > 100) { throw new Exception('Invalid integer format'); } // Validate $_GET['id'] contains only 0 to 9 chars if (strspn($_GET['id'], '0123456789') !== strlen($_GET['id'])) { throw new Exception('Invalid integer format'); } $id = $_GET['id']; $sql = 'SELECT * FROM sample WHERE num = $1'; $ret = pg_query_params($sql, [$id]); if ($ret === FALSE) { throw new Exception('Query failed'); } ?>
符号あり整数をバリデーションする場合、以下のようなコードが利用できる。
// There should be arbitrary length limit validation if (strspn(strlen($_GET['id']) > 100) { throw new Exception('Invalid integer format'); } // Validate $_GET['id'] contains only 0 to 9 chars and leading +-. $start = ($_GET['id']{1} === '+' || $_GET['id'] === '-') ? 1 : 0; if (strspn($_GET['id'], '0123456789', $start) !== strlen($_GET['id']) - $start) { throw new Exception('Invalid integer format'); }
このような単純な文字列チェックには正規表現を避け、文字列関数を使用するべである。
例外
整数が確実に符号付き32ビット整数の範囲内であることが保証できる場合は整数にキャストしても構わない。
リスク評価
ある日突然キャストによる整数オーバーフローでシステムが利用できなくなる、32ビット環境では仕様よりも小さい整数しか取り合使えない、などの問題が発生する。
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| IDS05-J | medium | likely | low | P4 | L3 |
