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

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

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

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

Q&A

解決済

6回答

5305閲覧

プリペアードステートメントの必要性について

qopllqopllqop

総合スコア36

PHP

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

4グッド

8クリップ

投稿2016/05/07 22:20

###質問
プリペアードステートメントはSQLインジェクションを防ぐための手段の一つとして有効だと思いますが、POSTやGETで外部から変数を取り込んで処理しない場合は、変数を使用していても使う必要はないんじゃないかな?と感じています。

例えば、セッションからユーザーidを取り出し、そのユーザーIDで絞り込んだSQLを走らせるような場合です。

SQLで変数を使用する場合は、必ずプリペアードステートメントを使用すると考えていましたが、SQLインジェクションを受けないような箇所では、必要ないのでしょうか?それとも、脆弱性となりうるのでしょうか?

自身でも調べましたが、思うように情報にたどり着けませんでした。
皆様のご経験からご教授もしくは、説明されているサイトをご紹介いただければ幸いです。よろしくお願いいたします。

dsk, iwamoto_takaaki, manabufukai, ikuwow👍を押しています

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

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

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

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

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

guest

回答6

0

ベストアンサー

さて、下記のようなことが考えられますが、それでも安全と言えますか?

  1. 外部からの入力が無く安全だと思っている変数について、外部から任意の値には絶対にできないという保証はどうやってするのでしょうか?たとえば、今回はセッションからユーザーIDを取得するとありますが、セッションから取得するときに、どうやっても外部からの入力が無いことを保証するには、その部分を詳しく掘り下げていくしかありません。それはかなりの苦労が必要になることでしょう

  2. ソース上、安全だと判断できたとして、そこにバグが無いと言うことをどうやって保証するのでしょうか?気付かなかった、バグで外部から任意の値にできてしまう可能性を限りなく0に近づけることはできても完全に0にすることは不可能です

  3. 今は安全で、バグもないっぽいですが、将来の修正や改変で、外部から任意の値を入力できてしまうと言ったことが絶対に行われないようにする方法は存在しますでしょうか?はっきり言います。どんなにドキュメントにしつこく書いたとしても人は間違えるものます

  4. 似たような処理を新たに作るときに、そのSQLをコピペして流用することは無いと言いきれますでしょうか?そして、それらに使われる変数が同じように安全であるという保証はあるのでしょうか?ここで使っているから、こっちで使っても大丈夫だろうって思ってしまうのが人のさがです

結局、安全であるという保証は一過性のものか限定されたものにすぎず、SQLインジェクションの危険性を完全に払拭できる物ではありません。それなら初めから、何が来ても大丈夫なように書いた方が安全です。常に、必ず、プリペアードステートメントを使用した方が、安全なプログラムと言えるでしょう。

攻撃に対する防御は一つあれば十分ということはありません。その一つにに不具合があって、破られてしまったら終わりです。二重、三重の防御壁や、例え破られたとしても影響範囲を最小限に絞る仕組みを取り入れる、などと言ったことが必要です。ただ、中にはコストや手間がかかる物もあります。そこら辺は採用しなかった場合のデメリットとのトレードオフになります。しかし、プリペアードステートメントのような、コストも手間も必要がない物を採用しないという選択肢はありません。必要・不必要という判断では無く、可能な限りより安全な方を採用すべきです。

投稿2016/05/07 23:39

raccy

総合スコア21735

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

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

qopllqopllqop

2016/05/08 03:57

ご回答ありがとうございます。とても詳しくご説明いただき、ありがたい限りです。今後起こりうる可能性と、予期しないことの可能性をなるべく減らすということですよね。 プログラミングの基礎的な考え方として勉強になりました。 ありがとうございました。
guest

0

間違えているかもしれませんが、私の理解では prepared statement というものが考案されたのは、繰り返し実行される SQL に関する「効率面」からの要請であって、「セキュリティ面」は「おまけ」です。つまり、SQL が実行されるまでには、ざっくりと言っても字句解析→構文解析→実行計画策定→実行、という段階がありますが、prepared statment は構文解析までを先に済ませて無駄な計算を減らそう、というものです。SQL インジェクションは構文を変えてしまうような攻撃方法ですから、構文木ができあがっていれば攻撃が成立しない、というたいへんありがたい「おまけ」がついてくる、という理解です。

セキュリティ面に関しましても、たとえば、WEB サーバ (apache や nginx など) の脆弱性を突かれてファイルシステム上のセッション情報が書き換えられる、というケースも考えられます。
他に考えうる具体的なセキュリティリスクは他の方が回答してくれると期待していますので割愛しまう。

ブラウザから送られてくる情報を DB 問合せに用いるときだけ prepared statement を使う、というような設計・コーディング規約より、変数が含まれるときは必ず prepared statement を使う、というほうが間違いが起こりにくいと思います。そのほうがコードを読みやすいでしょうし、今までセッションから引いて DB に渡す変数としていた値をクッキーから取るように変更、とした場合に、prepared statment に置き換えるのを忘れた、というようなミスもなくなります。

アクセス数やデータサイズが非常に大きい場合など、prepared statement の方がかえって遅くなるケースもあるかもしれませんが、そういう場合は prepared statement をどう使うかという個別の細かいところいにこだわるより、総合的な DB チューニングが必要なケースでしょう。

投稿2016/05/07 23:15

unau

総合スコア2468

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

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

qopllqopllqop

2016/05/08 04:00

ご回答ありがとうございました。もともとはセキュリティ用に考えられたものではなく、効率面から考えられたものだったのですね。同じ処理を繰り返すとき、処理速度的にもメリットがありそうですね。 あとはヒューマンエラーを想定してなるべくリスクを避けるという考え方ですね。 とても勉強になりました。ありがとうございました。
guest

0

既に多くの優れた回答がありますが、関心のあるテーマですので屋上屋を承知でコメントさせて下さい。
背景として、以下があると考えます。

  • どこか一箇所でもSQLインジェクションがあるとDBのデータがすべて漏洩する
  • このためSQLインジェクションは致命的な脆弱性であり、絶対に許容できない
  • プログラムにはバグがつきものであり、脆弱性もバグの一種である以上ゼロにはできない
  • しかし、上記の事情からSQLインジェクションはゼロにしたい・するべし

このため、人間が不注意でミスを犯しやすいという前提で、それでもSQLインジェクションをゼロにする方法論が欲しいところです。
そのような方法はあります。それは、

  • SQL文を文字列連結で組みたてない

ということです。そのためには、SQL文中の動的なパラメータはプレースホルダとして指定するしかありません。
つまり、SQLインジェクション対策のためにプリペアードステートメントを使うというのとはちょっと違いまして、

  • SQL文を文字列連結で組み立てなければSQLインジェクションの余地はない
  • そのためにはプレースホルダを用いて動的パラメータを指定する必要がある

という流れなのです。

また、以下の様な考え方もあります。
それは、脆弱性対処をできるたけ局所的に閉じ込めたいという動機です。
つまり、SQL呼び出しをしているところをミクロに確認して、SQLインジェクションがないことを確認したいという動機です。それができれば、プログラムを書くときも、後からチェックするときも、セキュリティ対処の負担を減らすことができます。
対象がユーザーIDであればSQLインジェクションにならないことはすぐわかると思うかもしれませんね。しかし、そこには何らかの「判断」が入ります。例えば5行前を確認すれば済むことかもしれませんが、それすらやりたくないのです。
また、ユーザーIDというと暗黙に英数字のみで構成されたものを連想しますが、一般的にはユーザーIDとしてメールアドレスを用いる場合も多いです。メールアドレスであれば、SQLインジェクション攻撃は可能です。

参考: XSSとSQLインジェクションの両方が可能なRFC5322適合のメールアドレス

したがって、「ユーザーIDであればSQLインジェクション攻撃はできない」と決めつけることも危険だと考えます。

まとめますと、SQLインジェクションが極めて危険な脆弱性であるがゆえに、SQLインジェクション脆弱性が購入する可能性を極限まで少なくしたいこと、そのためにはSQL文を文字列連結で組み立てないことを徹底すればよい、そうするにはプレースホルダを使うしかない、というのが「SQLインジェクション対策にはプリペアードステートメントを使え」ということの背景にある理由だと思います。

以下の記事も参考にしていただければと思います。
SQLインジェクション対策の極意はSQL文を組み立てないことにあり

投稿2016/05/10 12:34

ockeghem

総合スコア11701

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

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

qopllqopllqop

2016/05/10 12:59

素晴らしいご意見をいただき、ありがとうございます。ふと感じた疑問がこれほど多くの方に見ていただけるとは思いませんでした。と同時に同じ疑問を持たれている方も多くいらっしゃったのかもと感じました。 ユーザーIDが内部処理であっても安全とは言えないというのはよくわかりました。内部であれ外部であれ、脆弱性対策は欠かせませんね。 誰かに質問されても、これでわかりやすく答えることができそうです。 ありがとうございました。
guest

0

SQLで変数を使用する場合は、必ずプリペアードステートメントを使用すると考えていましたが、SQLインジェクションを受けないような箇所では、必要ないのでしょうか?

ご推察のとおりです。

何でもかんでもプリペアードステートメントで対応するというのは誤った考え方です。

安全のみならず安心も考えた対応が望ましいです。

運用で不具合が出た際、データの整合性・正当性のチェックのため、ログに吐かれたSQL文を
よく使うことになります。
この際、?は人力で変更を掛けて、DBにクエリを投げて確認するという手法になります。

1つや2つであれば、それほどの苦労ではないかもしれません。

しかし、一連のロジックの流れの中で投げているSQL文全部を調査するとなると、
sqlは10本を超え、?の読み替えは数十本になるという可能性は大いにあります。

これを緊急にやらないといけないことを考えれば、
「何でもかんでもプリペアードステートメント」
では、保守者の負担を増大させている事に気づけるかと思います。

それなので、SQLインジェクションの不安があるけど、使いたくない場合は、
チーム内で相談されて対応決めるのがベストかと思います。

投稿2016/05/08 05:51

TetsujiMiwa

総合スコア1124

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

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

qopllqopllqop

2016/05/08 11:09

ご意見ありがとうございます。まさに仰られているとおり、なんでもかんでも盲目的に使うとなると、その必要性はあるのか?というのが今回の質問の論点です。 その意味では、僕のスキルよりもずっと高い見識で、闇雲はどうかと思うというご意見を頂けて、腑に落ちました。 未知の攻撃に対する備えが必要とは思いますが、正しい判断を心掛けるという考えはTetsujiMiwaさんのご意見に、個人的ベストアンサーです。
guest

0

私なら・・・・ですが、

  1. 今後の展開でrequestから値を入れることがあるかも
  2. prepared statmentで行う部分とそうでない部分が一つのシステムに混在するのはコーディング規約的に嫌
  3. prepared statmentでやる場合のデメリットが感じられない

以上の理由からprepared statmentで組み込みますね。
セキュリティ的なことを考えるなら、大丈夫だからと言って敢えてセキュリティ問題を起こしそうなロジックを入れるというのは最初に避けて通りますね。
備えあれば・・・の精神ですね。

投稿2016/05/08 01:26

T.Yokotani

総合スコア141

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

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

qopllqopllqop

2016/05/08 03:55

ご回答ありがとうございます。とてもわかりやすくまとめていただいありがとうございます。今後、プリペアードステートメントが必要になるかも知れないということと、備えあれば憂いなしということですね。 プログラミングの考え方で、なるべくリスクを避ける作りにするということが良く理解できました。
guest

0

bind(プリペアードステートメントで変数を定義する)で、高速化できるメリットがあります。

一部のDBMS(Oracle,SQL server)では、SQLを実行する際にその実行計画(実行プラン)をキャッシュしてくれる機能があります。
例えば、1000レコードINSERTするような場合、同じSQLでINSERTする値を変数化すると、キャッシュが効いて、実行するSQLの解析コストを1000回から1回に減らすことができます。

MySQLにはこの実行計画のキャッシュ機能がないので、質問者のおっしゃる通り、POST/GETパラメータやCookie値以外に、変数化するメリットはあまり感じません。

投稿2016/05/07 23:25

kurosuke

総合スコア80

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

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

qopllqopllqop

2016/05/08 03:58

ご回答ありがとうござます。同じ処理を行うときは速度的にもメリットがありそうですね。セキュリティだけでなく、処理速度の観点からもご意見いただきありがとうございます。 とても勉強になりました。
amadablam

2016/05/10 10:41 編集

メリットはある
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問