PDOにおけるSQLインジェクションの危険性とその回避についてまとめ

PHPのデータベース・アクセス・ライブラリPDOは、DB接続時の文字エンコーディング指定ができないため、文字エンコーディングの選択によっては、プレースホルダを使っていてもSQLインジェクション脆弱性が発生しうるという徳丸氏のブログ内容に対し、識者がその回避方法について試し、報告しています。
8
徳丸 浩 @ockeghem

日記書いた『ぼくがPDOを採用しなかったわけ(Shift_JISによるSQLインジェクション) 』 http://www.tokumaru.org/d/20100701.html#p01

2010-07-01 09:25:42
Ikeda Masakazu @ikepyon

@ockeghemさんの日記を見てふと思い出したがMySQLのJDBC大丈夫だっけ?\の取り扱いを妙な対応してたよなぁ

2010-07-01 10:01:23
HARUYAMA Seigo @haruyama

PDO::MYSQL_ATTR_DIRECT_QUERY を false RT: @ockeghem: 日記書いた『ぼくがPDOを採用しなかったわけ(Shift_JISによるSQLインジェクション) 』 http://bit.ly/aAKdEL

2010-07-01 10:37:59
徳丸 浩 @ockeghem

PDO::ATTR_EMULATE_PREPARESをfalseにすると、よさそうです RT @haruyama: PDO::ATTR_EMULATE_PREPARES も falseにしておく必要があるか

2010-07-01 10:50:32
HARUYAMA Seigo @haruyama

.@ockeghem 「PDOは接続時に文字エンコーディングを指定できない(指定しても無視される)」ってほんとうですか? mysql_real_connect() が my.cnfの [client]セクションを読むので, my.cnfを指定することで可能と認識していましたが.

2010-07-01 10:51:31
徳丸 浩 @ockeghem

PDO、小泉守義さんのブログによると、昔は名前つきプレイスホルダ使用時にエミュレーションしてたけど、今は、PDO::ATTR_EMULATE_PREPARESをfalseにすると、名前つきプレイスホルダでも、プリペアード・ステートメントになるようですね

2010-07-01 10:52:48
徳丸 浩 @ockeghem

パケットキャプチャする限りは、常にLatin1ですね。私がWindows上で試しているから、という可能性はあります。 RT @haruyama: .@ockeghem 「PDOは接続時に文字エンコーディングを指定できない(指定しても無視される)」ってほんとうですか?

2010-07-01 10:56:59
徳丸 浩 @ockeghem

.@haruyama ありがとうございます。問題はそこではなくて、PDOがPrepared Statuementのエミュレーションする時のパーサが文字エンコーディングを考慮するかどうかなんですね

2010-07-01 11:01:20
HARUYAMA Seigo @haruyama

.@ockeghem charset=が効かないのはFAQですね. my.cnf で指定してもだめですか?

2010-07-01 11:01:25
CHIBA Masahiro @nihen

@ockeghem 試してないですけれど、http://bit.ly/awE8ZR を読む限りではDBD::mysqlと同じようにREAD_DEFAULT_FILEを指定することによってcharset指定できるんじゃないすかね

2010-07-01 11:02:46
HARUYAMA Seigo @haruyama

.@ockeghem Cのmysql_real_escape_string()の第1引数に関係しています. PHPソースのmysql_handle_quoter() の中ですね

2010-07-01 11:05:09
CHIBA Masahiro @nihen

@ockeghem あ、libmysqlclientの動的プレースホルダじゃなくてPDOが独自にやってるのであれば結局意味ないのかな、、試してみるかな

2010-07-01 11:05:18
HARUYAMA Seigo @haruyama

.@ockeghem MySQLの中まではみてないのですが, 適当に[client]で文字セットが設定されていたり サーバが文字セットを強制していれば 適当な文字エンコーディングでのエスケープが行なわれると思うのですが

2010-07-01 11:06:53
徳丸 浩 @ockeghem

.@haruyama PDOがmysql_real_escape_string()を呼んでいるかどうかが問題になると思います。少し古いですが、https://www.codeblog.org/blog/moriyoshi/20061221.html

2010-07-01 11:09:17
徳丸 浩 @ockeghem

Windowsでは、PDO::MYSQL_ATTR_READ_DEFAULT_FILEが無効で、my.cnfが指定できないようです。Linux上で調べます RT @haruyama: …my.cnf で指定してもだめですか?

2010-07-01 11:20:10
HARUYAMA Seigo @haruyama

.@ockeghem pdoのSQLのパースがこのままなら Shift_JISの場合は書いてあるようにおかしくなりますね.

2010-07-01 11:21:53
HARUYAMA Seigo @haruyama

.@ockeghem pdo_sql_parser.re からは stmt->dbh->methods->quoter という形で mysql_handle_quoter()が呼ばれています(MySQLなら)

2010-07-01 11:23:23
徳丸 浩 @ockeghem

.@haruyama 遅ればせながらPDOのソースを見たら、mysql_real_escape_string()を呼んでいるようでもあるので、もう少し調べます

2010-07-01 11:23:32
Moriyoshi Koizumi @moriyoshit

@ockeghem どんな問題について話しているのか追えてないので的はずれなことをいってそうですが、mysql_real_escape_string() は、PDO の MySQL ドライバが入った当初から使われていました。

2010-07-01 11:26:21
徳丸 浩 @ockeghem

@moriyoshit ほう、それでもPDOは文字エンコーディングを考慮していなかったのでしょうか?

2010-07-01 11:27:46
徳丸 浩 @ockeghem

これです http://www.tokumaru.org/d/20100701.html#p01 RT @moriyoshit: @ockeghem どんな問題について話しているのか追えてない…

2010-07-01 11:29:09
Moriyoshi Koizumi @moriyoshit

@ockeghem 文字エンコーディングが考慮されないのは、MySQL の実装が古いなどの理由で、プリペアードステートメントが PDO 側でエミュレーションされる場合です。

2010-07-01 11:30:01
Moriyoshi Koizumi @moriyoshit

@ockeghem その場合、SQL が一旦 PDO でパースされ、プレースホルダが抽出されるのですが、このパーサが文字エンコーディングを考慮していません。

2010-07-01 11:30:10
徳丸 浩 @ockeghem

.@moriyoshit なるほど。すると、パーサが通った後のエスケープでは、文字エンコーディングを考慮しているということでしょうか?

2010-07-01 11:32:20
Moriyoshi Koizumi @moriyoshit

@ockeghem PHP 5.3.0 とリンクしている libmysqlclient のバージョンが古い可能性があります。 niMySQL は positional な placeholder をサポートしているので、この部分がなんちゃって、になることはありません。

2010-07-01 11:39:10