元ネタは
わずか数行で"ものすごいテーブル"に! - jQueryプラグイン「Flexigrid」
http://journal.mycom.co.jp/articles/2008/06/25/flexigrid/menu.html
の三番目のPageである
動作サンプル - JSON+Ajaxでソートなどを可能にしたテーブルリスト
http://journal.mycom.co.jp/articles/2008/06/25/flexigrid/002.html
あえて。あえて、書かれてるソースをまず列挙してから会話をしたい。
<?php function sanitizeTag($var) { if ( is_array($var) ) { $var = array_map("sanitizeTag", $var); } else { $var = htmlspecialchars(trim($var)); } return $var; } $postdata = sanitizeTag($_POST); $postdata['page'] = ( !$postdata['page'] ) ? 1 : $postdata['page']; $postdata['rp'] = ( !$postdata['rp'] ) ? 10 : $postdata['rp']; $postdata['sortname'] = ( !$postdata['sortname'] ) ? 'zip_code' : $postdata['sortname']; $postdata['sortorder'] = ( !$postdata['sortorder'] ) ? 'asc' : $postdata['sortorder']; try { $dbh = new PDO ( 'mysql:host=localhost;dbname=zip', 'root', // DB接続ユーザ名 'password' //DB接続ユーザパスワード ); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $sql_field_array = array ( 'zip_code', // 郵便番号 'pref_name', // 都道府県名 'city_name', // 市区名 'area_name' // 地名 ); $select_field_name = ''; for($n=0; count($sql_field_array) > $n; $n++) { if ( 0 < $n ) { $select_field_name .= ', '; } $select_field_name .= $sql_field_array[$n]; } $where = ($postdata['qtype'] && $postdata['query']) ? 'where ' . $postdata['qtype'] . ' like \''. $postdata['query']. '\'' : ''; $stmt = $dbh->query( 'select COUNT(*) from zip ' . $where ); $count = $stmt->fetchColumn(); $sql = 'select '. $select_field_name . ' from zip '. $where . 'order by ' . $postdata['sortname'] . ' ' . $postdata['sortorder'] . ' ' . 'limit ' . ( ( $postdata['page'] - 1 ) * $postdata['rp'] ) . ', ' . $postdata['rp']; $stmt = $dbh->prepare( $sql ); $stmt->execute(); $json = array ( 'page' => $postdata['page'] - 0, 'total' => $count, ); while( $row = $stmt->fetch(PDO::FETCH_ASSOC) ) { foreach($row as $key => $value) { $tmp[] = $value; } $json['rows'][] = ( array ( 'cell' => $tmp ) ); unset($tmp,$key,$value); } $stmt = null; echo json_encode($json); } catch (PDOException $e) { var_dump($e); } ?>
で、まずい部分を突っ込み。
まず
$var = htmlspecialchars(trim($var));
これ。識者がどれだけか口を酸っぱくしてこの問題を声高に叫んでいる事か。
とりあえず、個人的にはこちらのpageのURIをあげさせていただきたく。
htmlspecialchars/htmlentitiesの正しい使い方
http://blog.ohgaki.net/htmlentitiesa_raspa_a_afia_a_s
htmlspecialcharsとhtmlenties関数はENT_QUOTESを指定しないとENT_COMPAT(セキュリティ上問題があるが互換性を維持)が指定された状態と同じ動作をします。
だ〜か〜ら〜「一枚ラップしろと」あれほどに以下略。trimも色々問題出そうだけど突っ込む気力もございませぬ。
ちなみにMWだとsecurityクラスでラップしておりますってのは余談。
つぎ。
$postdata = sanitizeTag($_POST);
これのまずい部分がわかるでしょうか?
答えは
・エスケープは使う直前に仕様用途に合わせて
という原則が完全無欠に無視されてるから。
で、上述がまずい事がよくわかる続編が、このコード。
$where = ($postdata['qtype'] && $postdata['query']) ? 'where ' . $postdata['qtype'] . ' like \''. $postdata['query']. '\'' : ''; $stmt = $dbh->query( 'select COUNT(*) from zip ' . $where );
とか
$sql = 'select '. $select_field_name . ' from zip '. $where . 'order by ' . $postdata['sortname'] . ' ' . $postdata['sortorder'] . ' ' . 'limit ' . ( ( $postdata['page'] - 1 ) * $postdata['rp'] ) . ', ' . $postdata['rp']; $stmt = $dbh->prepare( $sql );
とか。
…あのですね。
以前、 http://d.hatena.ne.jp/gallu/20070628/p1 に書きましたが。
id:elfさんが書かれてますが。
http://d.hatena.ne.jp/elf/20070628/1183004732
$value = htmlspecialchars($value);
$sql = 'INSERT ... '.$value.'...';
とかよくみかけます.
いやSQL Injectionは防げるかもしれないけど(苦笑 「なぜエスケープしないといけないか?」ということを考えていない andor 理解していないことを,この1〜2行でこちら側に理解させるというコードですね.
こんな話が「失笑レベルの笑い話として」出ているわけですよ。
ちなみに。シングルクォートがエスケープされていない時点でSQL-Injection防げません orz
っつかね。とりあえずさぁ
$s = '"\';'; print $s . "\n"; $s = htmlspecialchars($s); print $s . "\n";
こーゆーミニマムコード書いてみ? 何起きるかわかるべ?
わかんなきゃ周りの中にいる賢人に質問してみるべし。
結局のところ。XSSバリバリのSQL-Injectionダラリンコの、ソースとしては「最低」なレベルなわけです。
ちなみに。上述見つけた時点で、それ以上のチェックしてません。多分、ほかにも宝は埋もれてるんじゃないかと推測したりしなかったり。
まぁ一応。ご意見ご感想に連絡は入れましたけどね。この記事教えてくれた人も連絡いれたらしいですけどね。
…反応があったら追記しま。
とりあえず。頼むからこんな駄プログラム書かないでください*1 orz
っていうか。先日のZDNetでも思ったですが。ちったぁまともなレビュアーを入れるとか、せめて「ベータ版公開で広く突っ込まれてみてから本番公開」とか、なにがしかもうちょっと方法ってないもんですかねぇ?