« 見やすいPHPコードの書き方(但し我流) | トップページ | Googleカレンダーに関するまとめ »

ereg使っちゃいけません(ダメダメ♪ダメーッ)

まぁ、いままでereg多用しとったわけです。
ereg関数はPHP6.0でコアから消える予定【PHP】 - Programming Magic
そんなわけでereg厨の俺涙目状態、なわけです。

というか、実はeregにはさらにヤバい問題がありまして。
23. 関数とバイナリセーフ - PHP TIPS:ITpro
こちらの説明がわかりやすいので読んでいただければ、どういうことかは理解できると思います。
Cから入ってる人はひっかかりやすい部分だと思います。

とりあえず、実際にコードかいてみました。

<?php
header( "Content-type: text/html ; charset=utf-8" ) ;
?>
<HTML>
<TITLE>TEST</TITLE>
<BODY>
<P>テスト。
<P>\0 を途中に入れるとどうなる?
<P>\0 あり
<?php
$u = "1234567890" . "\x00" . "HOGEHOGE" ;
print "<P>PRINT raw = " . $u . "<br />\n" ;
print "htmlspecialchars = " . htmlspecialchars( $u ) . "<br />\n" ;
print "strlen = " . strlen( $u ) . "<br />\n" ;
print "is_numeric = " . var_export( is_numeric( $u ) , TRUE ). "<br />\n" ;
print "ctype_digit = " . var_export( ctype_digit( $u ) , TRUE ) . "<br />\n" ;
print "ereg(\"[^0-9]+\") = " . var_export( ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "mb_ereg(\"[^0-9]+\")=" . var_export( mb_ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "preg_match(\"/[^0-9]+/\")=" . var_export( preg_match( "/[^0-9]+/" , $u ) , TRUE ) . "<br />\n" ;
?>
<P>\0 なし
$u = "1234567890HOGEHOGE";
print "<P>PRINT raw = " . $u . "<br />\n" ;
print "htmlspecialchars = " . htmlspecialchars( $u ) . "<br />\n" ;
print "strlen = " . strlen( $u ) . "<br />\n" ;
print "is_numeric = " . var_export( is_numeric( $u ) , TRUE ). "<br />\n" ;
print "ctype_digit = " . var_export( ctype_digit( $u ) , TRUE ) . "<br />\n" ;
print "ereg(\"[^0-9]+\") = " . var_export( ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "mb_ereg(\"[^0-9]+\")=" . var_export( mb_ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "preg_match(\"/[^0-9]+/\")=" . var_export( preg_match( "/[^0-9]+/" , $u ) , TRUE ) . "<br />\n" ;
?>
<P>\0 なし、数字のみ
$u = "1234567890";
print "<P>PRINT raw = " . $u . "<br />\n" ;
print "htmlspecialchars = " . htmlspecialchars( $u ) . "<br />\n" ;
print "strlen = " . strlen( $u ) . "<br />\n" ;
print "is_numeric = " . var_export( is_numeric( $u ) , TRUE ). "<br />\n" ;
print "ctype_digit = " . var_export( ctype_digit( $u ) , TRUE ) . "<br />\n" ;
print "ereg(\"[^0-9]+\") = " . var_export( ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "mb_ereg(\"[^0-9]+\")=" . var_export( mb_ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "preg_match(\"/[^0-9]+/\")=" . var_export( preg_match( "/[^0-9]+/" , $u ) , TRUE ) . "<br />\n" ;
?>
</BODY>
</HTML>

¥x00(¥0)の文字を含むものを間に入れてみると、入れてないとで、動作が変わります。

テスト。

\0 を途中に入れるとどうなる?

\0 あり
PRINT raw = 1234567890HOGEHOGE
htmlspecialchars = 1234567890HOGEHOGE
strlen = 19
is_numeric = false
ctype_digit = false
ereg("[^0-9]+") = false
mb_ereg("[^0-9]+")=1
preg_match("/[^0-9]+/")=1

\0 なし
PRINT raw = 1234567890HOGEHOGE
htmlspecialchars = 1234567890HOGEHOGE
strlen = 18
is_numeric = false
ctype_digit = false
ereg("[^0-9]+") = 1
mb_ereg("[^0-9]+")=1
preg_match("/[^0-9]+/")=1

\0 なし、数字のみ
PRINT raw = 1234567890
htmlspecialchars = 1234567890
strlen = 10
is_numeric = true
ctype_digit = true
ereg("[^0-9]+") = false
mb_ereg("[^0-9]+")=false
preg_match("/[^0-9]+/")=0


本来は「¥0あり」「¥0なし」で同じ値を返さないとなりませんが、eregだけ違う値が返ってきてますね。
たとえばrequest.phpで引数pにid(INT)を渡すとします。こういうコード書きますよね。
<?php
$u = $_GET['p'] ;
if ( ereg( "[^0-9]" , $u ) ) ) {
// error !
} else {
// ok ! go !
$query = "SELECT * FROM table01 WHERE id=$u" ;
mysql_query( $query , $connection ) ;
(以下略)

で、リクエストを投げるときにこんなものを投げます。
http://example.com/request.php?p=12345678%00%20union%20(以下略)

すると、$u には、 "1234567890¥0union (以下略)" という文字列が渡ります。
ereg( "[^0-9]+" , $u ) は(さっきの結果をごらんのとおり) FALSE がかえりますので、SQL文を作るところに飛んできます。
$queryで展開されるのは、"SELECT * FROM table01 WHERE id=1234567890¥0 union (以下略)" ・・・。
はい・・・オワタ\(^o^)/、ですね。
そういうことです。

ちなみにこの系列で同様の影響を受けるのが以下の関数。これらはPHP6.0でereg()同様に廃止となります。
・ereg_replace()
・eregi()
・eregi_replace()
・split()
・spliti()
ちなみに正規表現を使わない場合、split()ではなくexplode()を使うといいです(そのぶん高速、かつバイナリセーフ)

この場面の・・・いや、今後の対策としては、
・mb_ereg() にする (いちばん簡単かつ確実)
・preg_match() をはじめとした PCRE関数に転向 書き換える( /〜/ を入れる必要があるので面倒)
・数字なら、is_numeric() とか ctype_digit() とかを使うようにする
あたりでしょうか。

参考:PHPネタ関連メモとか

|

« 見やすいPHPコードの書き方(但し我流) | トップページ | Googleカレンダーに関するまとめ »