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

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

ただいまの
回答率

90.32%

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

解決済

回答 6

投稿

  • 評価
  • クリップ 8
  • VIEW 3,199

qopllqopllqop

score 31

質問

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

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

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

checkベストアンサー

+21

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

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

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

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/08 12:57

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

    ありがとうございました。

    キャンセル

+18

間違えているかもしれませんが、私の理解では 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/08 13:00

    ご回答ありがとうございました。もともとはセキュリティ用に考えられたものではなく、効率面から考えられたものだったのですね。同じ処理を繰り返すとき、処理速度的にもメリットがありそうですね。

    あとはヒューマンエラーを想定してなるべくリスクを避けるという考え方ですね。
    とても勉強になりました。ありがとうございました。

    キャンセル

+6

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

  • どこか一箇所でも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 21:59

    素晴らしいご意見をいただき、ありがとうございます。ふと感じた疑問がこれほど多くの方に見ていただけるとは思いませんでした。と同時に同じ疑問を持たれている方も多くいらっしゃったのかもと感じました。


    ユーザーIDが内部処理であっても安全とは言えないというのはよくわかりました。内部であれ外部であれ、脆弱性対策は欠かせませんね。

    誰かに質問されても、これでわかりやすく答えることができそうです。
    ありがとうございました。

    キャンセル

+1

私なら・・・・ですが、

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/08 12:55

    ご回答ありがとうございます。とてもわかりやすくまとめていただいありがとうございます。今後、プリペアードステートメントが必要になるかも知れないということと、備えあれば憂いなしということですね。

    プログラミングの考え方で、なるべくリスクを避ける作りにするということが良く理解できました。

    キャンセル

0

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/08 12:58

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

    キャンセル

  • 2016/05/10 19:41 編集

    メリットはある

    キャンセル

0

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

ご推察のとおりです。

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

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

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

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/08 20:09

    ご意見ありがとうございます。まさに仰られているとおり、なんでもかんでも盲目的に使うとなると、その必要性はあるのか?というのが今回の質問の論点です。

    その意味では、僕のスキルよりもずっと高い見識で、闇雲はどうかと思うというご意見を頂けて、腑に落ちました。

    未知の攻撃に対する備えが必要とは思いますが、正しい判断を心掛けるという考えはTetsujiMiwaさんのご意見に、個人的ベストアンサーです。

    キャンセル

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

  • ただいまの回答率 90.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る