質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
PDO

PDO(PHP Data Objects)はPHPのデータベース抽象化レイヤーです。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

4回答

5517閲覧

PHPのPDOプレースホルダで

Everonward

総合スコア24

PDO

PDO(PHP Data Objects)はPHPのデータベース抽象化レイヤーです。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

5クリップ

投稿2017/08/04 06:47

PHPのPDOで、名前付けされたプレースホルダを利用しています。

PHP

1 2 $ShopID= "1"; 3 $sql ="SELECT 4 , ( Select Count(*) From Answer Where ShopID=:ShopID1 and Answer=1) as '1' 5 , ( Select Count(*) From Answer Where ShopID=:ShopID2 and Answer=2) as '2' 6 FROM Answer" ; 7 $arrPrepare = array(":ShopID1" =>$ShopID,":ShopID1" =>$ShopID); 8 $pdo->prepare($sql); 9 $pdo->execute($arrPrepare);

↑の例で、「:ShopID1」「:ShopID2」は、実は同じIDを与えたいのですが、
この場合もプレースホルダには、個別の名前を付けて、複数の要素に分けて指定しなければならないでしょうか?

同じIDを与えるのであれば、一括で指定する方法ってあったりするのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

SET NAMES sjis など極めて稀な設定にしない限りは原則的に安全なので,極端なエッジケースを取り上げて「使わないのが原則」とするのは賛同しかねます。 sjis はおろかそもそも SET NAMES なんてまず使いませんからね,普通は。

動的プレースホルダで脆弱性が発生する条件

それよりは,挙動の違いに着目したほうがいいです。

エミュレーションに関するまとめ

項目エミュレーションONエミュレーションOFF
パフォーマンス
SET NAMES による安全性
NULL値をそのままの型で受け取る
**数値をそのままの型で受け取る **(mysqlndのみが対象)×
複数の同名プレースホルダ×
PDO::PARAM_* 定数による正しいキャスト×
PDOStatement::bindParam メソッドによる副作用問題×
複文の実行

特に注意すべきは以下の項目です。

項目エミュレーションONエミュレーションOFF
**数値をそのままの型で受け取る **(mysqlndのみが対象)×
複数の同名プレースホルダ×
PDO::PARAM_* 定数による正しいキャスト×
複文の実行
数値をそのままの型で受け取る

MySQLでエミュレーションをOFFにした場合,フェッチしてくる数値を整数型で取得できる特性があります。それ以外の場合は文字列になってしまいます。

複数の同名プレースホルダ

すでに他の回答中で述べられている通りです。

PDO::PARAM_* 定数による正しいキャスト

エミュレーションをOFFにした場合,定数で指定した型に強制的にキャストされる特性があります。ONの場合,明示的なキャストが必要です。たとえば PDO::PARAM_INT を指定しても,実際の値が文字列の場合は PDO::PARAM_STR の場合と同じ動作になってしまいます。

複文の実行

エミュレーションがONの場合,セミコロン区切りで複数のSQL文が実行できる特性があります。

投稿2017/08/04 17:32

mpyw

総合スコア5223

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mpyw

2017/08/05 04:22

あと…静的プレースホルダにおいては,「CREATE TABLE」など,一部使えない構文が存在します。一方動的プレースホルダは,そもそも文として解釈せずにプレースホルダの単純置換のみを行うため,どんな構文にも適用できます。
suzukis

2017/08/05 08:15

ライブラリが参照している文字エンコーディングが不適切なときにエスケープが適切に行われないのが問題で、SET NAMESを使う事が直接問題を引き起こしているわけではありません。残念ながら誤った解説をしている書籍やサンプルはまだ多数出回っており、「極端なエッジケース」どころか試行錯誤でやっていると簡単にはまる罠だと思います。
mpyw

2017/08/06 15:41 編集

>> ライブラリが参照している文字エンコーディングが不適切なときにエスケープが適切に行われないのが問題で、SET NAMESを使う事が直接問題を引き起こしているわけではありません。 はい,読んでいただければわかると思いますが,まさにそのように拙著のQiita記事で記述しております。 ・「DSNでのcharset指定」の代わりに「SET NAMES」を使ってしまうと,クライアント側のエンコーディングとサーバ側のエンコーディングで相違が生じてしまう。 ・ただし,utf8 は latin1 と混同されても,文字コードの性質上,原則的に脆弱性は発生し得ない。 ・ただし,PHP5.3以前で使われるlibmysqlclientのコンパイルオプションを変更するという非常に稀な設定を敢えて行っている場合には,utf8 でも脆弱性になり得る。PHP5.4以降デフォルトのmysqlndを使うなら何の問題も無い。 「SET NAMES utf8」に関しては低レベルな書籍あるあるですが,「SET NAMES cp932」は入門書では一度も見たことがないです。古いシステムを引きずっている現場で使われていたり,セキュリティ専門家の方が脆弱性を指摘するために取り上げているだけだと思います。 そして「SET NAMES utf8」でも脆弱性が発生しうるパターンは非常に稀で,入門書にこんなマイナーなコンパイルオプションの設定に関する説明が書いてあるとは考えにくいです。それゆえに,実質 utf8 の場合は安全と断言して問題ないと考えます。 セキュリティ面だけを理由に,静的プレースホルダの使用を推奨する時代は終わってきていると思います。徳丸先生の書籍やブログが有名だと思いますが,これは初期のPDO実装があまりにも酷く,まだ枯れていなかったために厳しい意見が目立つだけで,現在では動的プレースホルダは十分枯れているといって問題無いと思います。 そもそも,「誤った解説をしている書籍やサンプル」の存在を前提とするのもミスリーディングです。そのような書籍やサンプルを淘汰していくように導くのが回答者の使命です。
suzukis

2017/08/06 13:01

文字エンコーディングが適切に設定されていない状態ではエスケープが適切に行われないのは原理的な問題で、これは今後も変わることはないでしょう。したがって、動的プレースホルダにはいつまでも潜在的なリスクが残り続けます。デフォルトでutf-8になり取り合えず使っている分には安全な状態、という状況になっていればよいのですが、現在はまだそうはなっていません。デフォルトで適切なエスケープが行えなる状態になるまでは、静的プレースホルダが第一選択であるべきです。
mpyw

2017/08/06 15:46 編集

>> デフォルトでutf-8になり取り合えず使っている分には安全な状態、という状況になっていればよいのですが、 デフォルトではクライアント・サーバともに latin1 で一致しているので安全です。文字化けするだけで脆弱性のリスクはありません。 (万が一どちらか一方のみが utf8 になった場合も問題ありません。問題が発生しうるのは sjis が絡んできた場合のみです)
suzukis

2017/08/07 00:31

Shift_JISで顕著に問題が起きることから例としてShift_JISが用いられることが多いのは事実ですが、問題の本質は「エスケープの際には文字コードを考慮した上で行う必要がある」ということです。この点を誤解されているのだと思います。latin1以外のデータをlatin1としてエスケープするよな状況は、結果的に問題がないとしても不適切な状態ですので許容すべきではありません。
退会済みユーザー

退会済みユーザー

2017/08/07 03:02 編集

横からすみません。 > ライブラリが参照している文字エンコーディングが不適切なときにエスケープが適切に行われないのが問題で、SET NAMESを使う事が直接問題を引き起こしているわけではありません。 ここのライブラリって、PDO を指していますか?SET NAMES (と SET CHARACTER かな?)以外の文字エンコーディングがズレる原因って、何を指しているのか気になったもので。
suzukis

2017/08/07 04:30

PDOではなく、libmysqlなどのDBの接続ライブラリです。エスケープの方式はDBによって異なるので、DBのライブラリがそれぞれエスケープの関数を持っています。もともと、このエスケープの関数は文字エンコーディングを考慮していないものでした。しかし、文字エンコーディングを考慮しないエスケープは問題があることがわかり、文字エンコーディングを考慮してエスケープを行う関数が後から追加されました。このとき使われる文字エンコーディングは接続情報から持ってきます。MySQLのSET NAMESがまずいと言われるのはSET NAMESがこの情報を変更しないためです。デフォルトはlatin1だったはずですので、SET NAMESを使おうが使うまいがlatin1としてエスケープされ、たいていの場合入力はlatin1ではないでしょうからまずいという話です。
退会済みユーザー

退会済みユーザー

2017/08/07 05:40

> suzukis さん 理解しました。そこを PDO と読んでいたので文脈がうまく追えず、ちょっと混乱していました。ありがとうございます。
mpyw

2017/08/07 16:58 編集

>> latin1以外のデータをlatin1としてエスケープするよな状況は、結果的に問題がないとしても不適切な状態ですので許容すべきではありません。 それを言うなら「charset=utf8」「charset=utf8mb4」などのDSN指定を使わない時点で不適切ですし,これを怠っているような参考書は「PDO::ATTR_EMULATE_PREPARES => false」の設定もしていないでしょう。DSN指定をサボったツケを動的プレースホルダに押し付けるのは話が脱線しすぎだと思います。そのレベルで ❝適切さ❞ について議論すること自体が不毛です。これはセキュリティの問題ではありません。
mpyw

2017/08/07 17:40 編集

少なくともPHPから使う以上はPDOを中心に考えないといけないので, 動的プレースホルダ: 安全に使うには条件がある 静的プレースホルダ: 原理的に安全 よりは PDOを安全に使うには以下のいずれか1つまたは両方を実施する ・DSNでcharsetを正しく指定する ・PDO::ATTR_EMULATE_PREPARESをfalseにする という説明のほうがフェアだと思います。静的プレースホルダって,デフォルトで使えるものではなく,これにも設定が必要ですからね。なので原則について語るならば 「原則的に静的プレースホルダを使用すべき」 と同じように 「原則的にDSNでのcharset指定を行うべき」 という説明もできると思います。どちらか一方だけが正解というわけではなく,どちらも正解です。したがって,静的か動的かで優劣をつけるのではなく,特徴を理解したうえでプロジェクトに合った方を使おう,というのが結論です。
guest

0

PDO::ATTR_EMULATE_PREPARES => true に設定されている場合は同一のプレースホルダで利用可能です。

投稿2017/08/04 06:51

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Everonward

2017/08/04 10:55

ありがとうございました!無事解決しました!
guest

0

PDO::ATTR_EMULATE_PREPARESをtrueにする(動的プレースホルダを使う)と言う回答が付いていますがPDOの動的プレースホルダは設定または環境に問題があるとSQLインジェクション脆弱性を生じますので使わないのが原則です。

  • 動的プレースホルダ→安全に使うには条件がある
  • 静的プレースホルダ→原理的に安全

なので、静的プレースホルダが「より安全」になるわけではありません。

詳しいことは「体系的に学ぶ 安全なWebアプリケーションの作り方」という本を一読されることをお勧めします。

投稿2017/08/04 07:28

編集2017/08/04 07:30
suzukis

総合スコア1449

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Everonward

2017/08/04 10:56

すごくよくわかりました。ありがとうございました!
guest

0

ベストアンサー

動的処理であれば一つの宣言で2箇所以上のbindができます

PHP

1 $pdo = new PDO($dsn, $user,$password); 2 $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,true); 3 $sql="SELECT * FROM tbl WHERE a=:x and b=:x"; 4 $stmt = $pdo->prepare($sql); 5 $stmt->bindValue(':x',10, PDO::PARAM_INT); 6 $stmt->execute(); 7

ただし、動的処理はPHP側でエスケープ処理をするためよりセキュアな処理を
希望するなら静的処理の方が望ましいとされています

投稿2017/08/04 06:57

yambejp

総合スコア114585

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Everonward

2017/08/04 10:55

ありがとうございました!無事解決しました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問