PHPからSET NAMESを使わない方が良い理由と対策まとめ
今更ながらyohgaki's blogのSET NAMESは禁止というエントリーを読みました。ためになりましたが、コメント内で話が進んでおり少々理解しづらかったため、改めてまとめてみました。
・PHPからSQL文でSET NAMESを発行すると何が良くないのか
SQLの実行により文字コードが変更されるように思えるが、PHPのMySQL関連の関数で考慮される文字コードは変更されない。このため、エスケープ関数mysql_real_escape_string()等で、必要なエスケープがなされず脆弱性を引き起こすケースがある。
・どうすれば安全なのか
PHP5.2.3以降の場合なら → mysql_set_charset()を使えばOK
PHP5.0.5以降でmysqliを使用しているなら → mysqli_set_charset()を使えばOK
どちらも使えない場合でも、
my.cnfで設定されている文字コード設定が使いたい文字コードと同じならOK
設定されている文字コードはPHP4.3以降に存在するmysql_client_encoding()で確認できる。
もし違った場合、
使いたい文字コードがUTF-8またはEUC-JPなら
入力文字列が正しい文字コードかチェックして、その上でmysql_escape_string()すればOK
文字コードのチェックにはPHP4.4.3またはPHP5.1.3以降ならmb_check_encoding()
それより古ければmb_convert_encoding()を使えば良い。
使いたい文字コードがShift_JISなら
入力文字列が正しい文字コードかチェックして、その上で自前のエスケープ用の関数を通せばOK
文字コードのチェックにはmb_check_encoding()またはmb_convert_encoding()を使えば良い。
自前のエスケープ用の関数では、Shift_JISの \x00, \n, \r, \, ', " そして \x1a をエスケープすれば良いはず。
・文字コードのチェック場所
PHPの古いバージョンを使っている場合、入力文字列のチェックが必要とのことですが、変数ひとつひとつをチェックするのは大変ですから、最終的なSQL文を一括チェックするか、ユーザーからの入力値を最初に一括チェックするのが良いと思います。
入力値を一括チェックする場合は、$_GETの値と、$_POSTの値(ただしファイルアップロードの値ははぶく)は一括でチェックということになりますでしょうか。
// 入力値のチェック例
foreach ($_GET as $name => $value) if (!mb_check_encoding($name, $encoding) || !mb_check_encoding($value, $encoding) exit;
// SQLのチェック例
if (!mb_check_encoding($sql, $encoding)) exit;
個人的には、もうPHP5.2.3以降しか使わないよ、てことでmysql_set_charset()を使って安泰です :-)
参考情報:
http://blog.ohgaki.net/set_namesa_mcb_asc#c15178
http://d.hatena.ne.jp/t_komura/20060122#c1139755460
Trackback URL for this post:
以下、 ギークなお姉さんは好きですか <title>をクールにしてみた! より...
