整数値のオーバーフローを検出/防止する
PHPの整数型はCPU/OSによって異る
- 64 bit OS - 整数型は符号付き64bit整数 (ただし、Windowsは7.0未満は符号付き32bit整数)
- 32 bit OS - 整数型は符号付き32bit整数
浮動小数点型はIEEE754の倍精度浮動小数点型で符号付き53bit整数まで正確に表現できる。
厳密な整数型の利用が必要な場合は整数値のオーバーフローを検出、防止が必要となる。特に外部システムとデータをやり取りする場合に、データ型と範囲の違いに注意しなければならない。整数型には符号付きと符号無し整数の2種類がある。PHPは符号付き整数型のみサポートするので、符号無し整数を取り扱う場合にも注意が必要である。
ここではRDBMSのPostgreSQLを例として利用する。しかし、データ型を持つシステム(PHPを含む)の場合はオーバーフローする場合の取り扱いに注意しなければならない。非適合コード、適合コードは以下のテーブル定義を持つデータベースとする。
CREATE TABLE sample ( id SERIAL NOT NULL, -- SERIAL is signed 32 bit integer int4 INT, -- INT is signed 32 bit integer int8 INT8 -- INT8 is signed 64 bit integer );
※ PostgreSQLは16/32/64bit符号付き整数型のみサポートする。MySQLは明示的に指定し符号無し整数型を利用できる。
非適合コード例
<?php $db = pg_connect('host=localhost'); if (!$db) { throw new Exception('Cannot connect to database'); } $myint = 1234567890*12345; // Exceeds signed 32 bit integer range, but within signed 64 bit integer range. $sql = 'INSERT INTO sample (int4, int8) VALUES ($1, $2)'; $ret = pg_query_params($sql, [$myint, $myint]); if ($ret === FALSE) { throw new Exception('Query failed'); } ?>
このコードは32bit/64bit OS両方のPHPでクエリに失敗する。
エラー例
Warning: pg_query_params(): Query failed: ERROR: value "9223372036854775" is out of range for type integer in - on line 10
Fatal error: Uncaught Exception: Query failed in -:12
Stack trace:
#0 {main}
thrown in - on line 12
適合コード例
PHPで整数オーバーフローを検出するのは比較的簡単である。PHPは整数オーバーフローを起こす場合は自動的に浮動小数点型に変換される。64bit環境では符号付き32bit整数の範囲外であるか、のチェックも必要である。
<?php $db = pg_connect('host=localhost'); if (!$db) { throw new Exception('Cannot connect to database'); } $myint = 1234567890*12345; // Exceeds signed 32 bit integer range, but within signed 64 bit integer range. // Check signed 64 bit integer overflow // PHP_INT_SIZE is available since PHP 5.0.5 if (PHP_INT_SIZE === 8 && is_float($myint)) { throw new Exception('64 bit integer overflow'); } // Check signed 32 bit integer overflow // Under 32 bit OS, $myint became float. Under 64 bit OS, check 32 bit integer range if (is_float($myint) || $myint < 0x80000000 || $myint > 0xEFFFFFFF) { throw new Exception('32 bit integer overflow'); } $sql = 'INSERT INTO sample (int4, int8) VALUES ($1, $2)'; $ret = pg_query_params($sql, [$myint, $myint]); if ($ret === FALSE) { throw new Exception('Query failed'); } ?>
例外
DBMSなど、外部システムの整数オーバーフローエラーに頼れる場合は外部システムのエラーに頼っても構わない。だたし、他の出力先への出力する場合は先に外部システムに出力してエラーを確認しなければならない点に注意する。
リスク評価
入力を受け付けた時点または計算を行った時点でチェックしないとシステムで常にエラーが発生し、サービス提供が不能になる場合も考えられる。
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| IDS05-J | medium | unlikely | high | P4 | L3 |
