| « gmailをgmail.comドメイン以外で使う! | IPv6でWormの脅威が軽減 » |
addslashesによるエスケープ処理は止めましょう
Link: http://d.hatena.ne.jp/hoshikuzu/20060211#P20060211PHPSQLINJECTION
セキュリティmemoにaddslashesよるエスケープ処理でSQLインジェクションが可能なるという記事を見つけました。
私のセミナーを聞いたことがある方は「addslashesによるエスケープ処理は止めましょう」と言っていた事を覚えているでしょうか? mysql_real_escape_string()やpg_escape_string()等のデータベース専用のエスケープ関数を使いましょう、とも話しています。
ちなみにSQLiteを使っている場合はaddslashesでエスケープ処理はNGです。もっと根本的に間違っています。SQLiteではMS SQL Server, Sybaseと同様「'」は「''」とシングルクオートでエスケープします。
基本には忠実に :)
追記: サーバとクライアントのエンコーディングが合っていないと問題が発生します。PostgreSQLの場合、SET文でクライアントエンコーディングを変えるのではなくpg_set_client_encoding()を利用してエンコーディングを変えないとならないはずです。今は時間がないので後日また調べてまとめるようにします。
9 comments
Comment from: komura [Visitor]
リンク先ではデータベース側で文字列を SJIS として処理している場合は、mysql_real_escape_string() や pg_escape_string() 等のデータベース専用のエスケープ関数を使用してもエスケープ処理が回避されてしまうことが書かれています。
PostgreSQL でも、SET client_encoding TO 'SJIS'; を実行した後、"\x95' OR a = a; --" のような文字列を渡して、pg_escape_string() でエスケープすると、SQL インジェクションを引き起こすことが可能であることを確認しました(PostgreSQL 8.0.6/8.1.3)。
0x9527 という文字は本来は SJIS としては認められないはずですが、PostgreSQL では SJIS のマルチバイト文字として処理されるようです。
PostgreSQL でも、SET client_encoding TO 'SJIS'; を実行した後、"\x95' OR a = a; --" のような文字列を渡して、pg_escape_string() でエスケープすると、SQL インジェクションを引き起こすことが可能であることを確認しました(PostgreSQL 8.0.6/8.1.3)。
0x9527 という文字は本来は SJIS としては認められないはずですが、PostgreSQL では SJIS のマルチバイト文字として処理されるようです。
2006/02/15 @ 01:58
なるほど。文字エンコーディングの違い問題が発生する典型的な例ですね。
時間が無くて試していませんが、
SET client_encoding TO 'SJIS';
接続先のDBサーバにこのSQLを送信すると、クライアントのエンコーディングとサーバのエンコーディングが別のエンコーディングになるので基本的に間違っています。
要するにこうするとクライアントライブラリ(libpq)はサーバ側がSJISを使っている、ということを知らずに処理する事になります。
PostgreSQLサーバはSJISエンコーディングをサポートしていないのでクライアント側でクライアントエンコーディング->サーバエンコーディングに変換していた、と思います。(ここの記憶が怪しいですが、SETでインジェクションが可能になるならこうなっているはず。libpqの接続構造体がエンコーディングを保持していた記憶は確かなはず)
文字エンコーディングの違いでXSSが発生するのと同じで、SQLインジェクションできるようになるのは普通(?)の事だと思います。
# もしかして普通はそう考えない?
時間が無くて試していませんが、
SET client_encoding TO 'SJIS';
接続先のDBサーバにこのSQLを送信すると、クライアントのエンコーディングとサーバのエンコーディングが別のエンコーディングになるので基本的に間違っています。
要するにこうするとクライアントライブラリ(libpq)はサーバ側がSJISを使っている、ということを知らずに処理する事になります。
PostgreSQLサーバはSJISエンコーディングをサポートしていないのでクライアント側でクライアントエンコーディング->サーバエンコーディングに変換していた、と思います。(ここの記憶が怪しいですが、SETでインジェクションが可能になるならこうなっているはず。libpqの接続構造体がエンコーディングを保持していた記憶は確かなはず)
文字エンコーディングの違いでXSSが発生するのと同じで、SQLインジェクションできるようになるのは普通(?)の事だと思います。
# もしかして普通はそう考えない?
2006/02/15 @ 05:43
正しい設定方法を書いていなかったですね。
pg_set_client_encoding関数を使ってクライアントエンコーディングを設定すれば問題なく処理されるはずです。これが正しく処理されていないならPostgreSQLのセキュリティホール :)
専用の関数は単純なラッパーの場合もありますが、意味があって専用関数になっている場合もあります。内部仕様を完全に把握していない場合は専用関数を使うべきと思います。
pg_set_client_encoding関数を使ってクライアントエンコーディングを設定すれば問題なく処理されるはずです。これが正しく処理されていないならPostgreSQLのセキュリティホール :)
専用の関数は単純なラッパーの場合もありますが、意味があって専用関数になっている場合もあります。内部仕様を完全に把握していない場合は専用関数を使うべきと思います。
2006/02/15 @ 05:46
mysql_real_escape_string() calls MySQL's library function mysql_real_escape_string, which prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a.
とあるので、MySQLもmysql_real_escape_stringを使っていればどんな文字列で大丈夫なように作ってあると思います。仕様通りのエンコーディング指定をしていてもダメならMySQLのセキュリティホールという事に。
とあるので、MySQLもmysql_real_escape_stringを使っていればどんな文字列で大丈夫なように作ってあると思います。仕様通りのエンコーディング指定をしていてもダメならMySQLのセキュリティホールという事に。
2006/02/15 @ 05:53
今日のところはちょっと時間不足なので、最後にkomuraさんコメントありがとうございます。
pg_set_client_encoding関数以外でエンコーディングを変えると問題になる事は知っていましたが(自分が実装した関数なので当たり前ですが)SETで変えるのと同じ、と思われている方もいるはずですね。非常に参考になりました。
時間が出来たらもう少し調べて確認してみたいと思います。
pg_set_client_encoding関数以外でエンコーディングを変えると問題になる事は知っていましたが(自分が実装した関数なので当たり前ですが)SETで変えるのと同じ、と思われている方もいるはずですね。非常に参考になりました。
時間が出来たらもう少し調べて確認してみたいと思います。
2006/02/15 @ 06:03
もう一つ対処方法を。
プリペアードクエリを使用すればエンコーディングが違っていても(本来これ自体問題なのですが)SQLインジェクションは出来ないはずですね。
エスケープ処理のミスのみでなく、エンコーディング設定のミスを防ぐためにもプリペアードクエリを使用した方が良いですね。
プリペアードクエリを使用すればエンコーディングが違っていても(本来これ自体問題なのですが)SQLインジェクションは出来ないはずですね。
エスケープ処理のミスのみでなく、エンコーディング設定のミスを防ぐためにもプリペアードクエリを使用した方が良いですね。
2006/02/15 @ 06:18
http://ilia.ws/archives/103-mysql_real_escape_string-versus-Prepared-Statements.html
Iliaさんも同じ事を書いているのですね。と思ったら、リンク先のブログにもリンクあるし。よく読まないといかんですね。
Iliaさんも同じ事を書いているのですね。と思ったら、リンク先のブログにもリンクあるし。よく読まないといかんですね。
2006/02/15 @ 06:29
Comment from: komura [Visitor]
遅くなってしまいましたが、pg_set_client_encoding( 'SJIS' ) を実行してから同様の事を検証してみました。
結果としては、
SET client_encoding TO 'SJIS';
を実行してから SQL を実行したのと同様の結果になりました(0x9527 が SJIS の文字として見なされ、SQL インジェクション可能)。
PHP 5.1.2 と PostgreSQL 8.1.3 で確認しました。
あと、ご指摘通り、pg_prepare() と pg_execute() によるプリペアードクエリでは問題ありませんでした(文字コードが SJIS の場合でも SQL インジェクションを起こせない)。
PHP 側でこれらの関数が導入されたのは PHP 5.1.0 以降のようですので、それより前のバージョンでは文字コードに SJIS を使用しないようにするしかないように思います。
結果としては、
SET client_encoding TO 'SJIS';
を実行してから SQL を実行したのと同様の結果になりました(0x9527 が SJIS の文字として見なされ、SQL インジェクション可能)。
PHP 5.1.2 と PostgreSQL 8.1.3 で確認しました。
あと、ご指摘通り、pg_prepare() と pg_execute() によるプリペアードクエリでは問題ありませんでした(文字コードが SJIS の場合でも SQL インジェクションを起こせない)。
PHP 側でこれらの関数が導入されたのは PHP 5.1.0 以降のようですので、それより前のバージョンでは文字コードに SJIS を使用しないようにするしかないように思います。
2006/02/18 @ 10:17
Comment from: 石井 [Visitor]
pg_set_client_encodingを呼ぶのと,SET client_encoding TO をするのは,PostgreSQL 7.4以降であれば同じです.違っていたのは7.3以前の話ですね.
2006/02/19 @ 23:44