PHP の is_int と is_numeric
2005年2月4日 コンピュータ2009年6月24日追記
この日記の内容は「当時こうした」というもので、
is_int の動作や使い方に関しては
他によりよいページがあると思います。
なお、「End_Of_SQL」で探してきた人は
http://diarynote.jp/d/10395/20071001.html
を見てください。
========================================
僕は基本的に型の強い言語の方が好きである。
(でも、コンパイラ言語よりインタプリタ言語が好き。)
PHP から PostgerSQL を使っているが、
ヒアドキュメントに変数を埋め込む形で
SQL 文を作って渡すようにしている。
で、SQL インジェクション を防止するため、
サニタイズしてから埋め込む。
文字列なら
NULL を考慮するときは
みたいにして。
で、問題は数値。特に整数値。
PHP は 型の弱い言語で通常は型を強く意識する必要はない。
最初にその変数に代入したときの値で内部に型を持ち、
必要なときに暗黙に型変換を行う。
is_numeric という関数はその変数が数値か数値文字列なら
TRUE を返す。
しかし、PHP の組み込み関数の is_int は
変数の内部に持つ型が int型かどうかを判定する。
SQL文に渡す前の前チェックで、
変数に is_numeric を噛ますことで
SQL インジェクションは防いでいた。
しかし、整数でなければならないところで
整数値かどうかのチェックをしていなかった。
で、実験してみた。
みたいなテーブルがある状態で
ってのはまぁいい。
int_field を numeric型にキャストして比較されるので。
問題は関数を使ったとき。
としてあるときに
とすると エラーが発生する。
実際、html の Form で
数値を直接入力させるところは無かったが、
SELECT で数値を選ぶところがあり、
Form を改竄して直接値を放り込むテストをしたところ、
PostgreSQL のエラーが出てしまった。
で、数値を入れて query を投げるときに
数値が整数かどうかをチェックすることにした。
でも、is_int ではだめ。
Form から送られてくるのを $_REQUEST から取り出して
変数に代入するので
内部型は整数ではない。
全部明示的に整数型にキャストすることも検討したが、
意図しない値に変わるのを嫌った。
で、
という関数を作った。
但し、これでは
の場合にエラーとなるので
チェック後に明示的に int型にキャストするようにした。
この日記の内容は「当時こうした」というもので、
is_int の動作や使い方に関しては
他によりよいページがあると思います。
なお、「End_Of_SQL」で探してきた人は
http://diarynote.jp/d/10395/20071001.html
を見てください。
========================================
僕は基本的に型の強い言語の方が好きである。
(でも、コンパイラ言語よりインタプリタ言語が好き。)
PHP から PostgerSQL を使っているが、
ヒアドキュメントに変数を埋め込む形で
SQL 文を作って渡すようにしている。
で、SQL インジェクション を防止するため、
サニタイズしてから埋め込む。
文字列なら
$foo = pg_escape_string($foo);
$SQL=<<End_Of_SQL
SELECT ’$foo’;
End_Of_SQL
NULL を考慮するときは
if(is_null()){
$foo = ’NULL’;
} else {
$foo = "’" . pg_escape_string($foo) . "’";
}
$SQL=<<End_Of_SQL
SELECT $foo;
End_Of_SQL
みたいにして。
で、問題は数値。特に整数値。
PHP は 型の弱い言語で通常は型を強く意識する必要はない。
最初にその変数に代入したときの値で内部に型を持ち、
必要なときに暗黙に型変換を行う。
$foo = 3;
echo $foo . ’ +1 は?’;
echo $foo + 1;
$foo = ’3’;
echo $foo . ’ +1 は?’;
echo $foo + 1;
結果:
3 +1 は?
4
3 +1 は?
4
is_numeric という関数はその変数が数値か数値文字列なら
TRUE を返す。
$foo = 3;
echo is_numeric($foo);
$foo = 3.0;
echo is_numeric($foo);
$foo = ’3’;
echo is_numeric($foo);
結果:
true
true
true
しかし、PHP の組み込み関数の is_int は
変数の内部に持つ型が int型かどうかを判定する。
$foo = 3;
echo is_int($foo);
$foo = 3.0;
echo is_int($foo);
$foo = ’3’;
echo is_int($foo);
結果:
true
false
false
SQL文に渡す前の前チェックで、
変数に is_numeric を噛ますことで
SQL インジェクションは防いでいた。
しかし、整数でなければならないところで
整数値かどうかのチェックをしていなかった。
で、実験してみた。
CREATE TABLE boo(int_field INTEGER UNIQUE);
みたいなテーブルがある状態で
$foo = 3.1
$SQL=<<End_Of_SQL
SELECT int_field FORM boo
WHERE int_field = $foo
End_Of_SQL
ってのはまぁいい。
int_field を numeric型にキャストして比較されるので。
問題は関数を使ったとき。
CREATE FUNCTION woo(INTEGER) RETURNS INTEGER AS
’SELECT $1’ LANGUAGE SQL;
としてあるときに
$foo = 3.1
$SQL=<<End_Of_SQL
SELECT woo($foo)
End_Of_SQL
とすると エラーが発生する。
実際、html の Form で
数値を直接入力させるところは無かったが、
SELECT で数値を選ぶところがあり、
Form を改竄して直接値を放り込むテストをしたところ、
PostgreSQL のエラーが出てしまった。
で、数値を入れて query を投げるときに
数値が整数かどうかをチェックすることにした。
でも、is_int ではだめ。
Form から送られてくるのを $_REQUEST から取り出して
変数に代入するので
内部型は整数ではない。
全部明示的に整数型にキャストすることも検討したが、
意図しない値に変わるのを嫌った。
で、
function is_int_numeric($var){
returns is_numeric(var) && ((int)$var == $var)
}
という関数を作った。
但し、これでは
$foo = 3.0
if(is_int_numeric($foo)){
exit();
}
$SQL=<<End_Of_SQL
SELECT woo($foo)
End_Of_SQL
の場合にエラーとなるので
チェック後に明示的に int型にキャストするようにした。
$foo = 3.0
if(is_int_numeric($foo)){
exit();
}
$foo = (int)$foo;
$SQL=
コメント