###質問
プリペアードステートメントはSQLインジェクションを防ぐための手段の一つとして有効だと思いますが、POSTやGETで外部から変数を取り込んで処理しない場合は、変数を使用していても使う必要はないんじゃないかな?と感じています。
例えば、セッションからユーザーidを取り出し、そのユーザーIDで絞り込んだSQLを走らせるような場合です。
SQLで変数を使用する場合は、必ずプリペアードステートメントを使用すると考えていましたが、SQLインジェクションを受けないような箇所では、必要ないのでしょうか?それとも、脆弱性となりうるのでしょうか?
自身でも調べましたが、思うように情報にたどり着けませんでした。
皆様のご経験からご教授もしくは、説明されているサイトをご紹介いただければ幸いです。よろしくお願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
0
ベストアンサー
さて、下記のようなことが考えられますが、それでも安全と言えますか?
-
外部からの入力が無く安全だと思っている変数について、外部から任意の値には絶対にできないという保証はどうやってするのでしょうか?たとえば、今回はセッションからユーザーIDを取得するとありますが、セッションから取得するときに、どうやっても外部からの入力が無いことを保証するには、その部分を詳しく掘り下げていくしかありません。それはかなりの苦労が必要になることでしょう。
-
ソース上、安全だと判断できたとして、そこにバグが無いと言うことをどうやって保証するのでしょうか?気付かなかった、バグで外部から任意の値にできてしまう可能性を限りなく0に近づけることはできても完全に0にすることは不可能です。
-
今は安全で、バグもないっぽいですが、将来の修正や改変で、外部から任意の値を入力できてしまうと言ったことが絶対に行われないようにする方法は存在しますでしょうか?はっきり言います。どんなにドキュメントにしつこく書いたとしても人は間違えるものます。
-
似たような処理を新たに作るときに、そのSQLをコピペして流用することは無いと言いきれますでしょうか?そして、それらに使われる変数が同じように安全であるという保証はあるのでしょうか?ここで使っているから、こっちで使っても大丈夫だろうって思ってしまうのが人のさがです。
結局、安全であるという保証は一過性のものか限定されたものにすぎず、SQLインジェクションの危険性を完全に払拭できる物ではありません。それなら初めから、何が来ても大丈夫なように書いた方が安全です。常に、必ず、プリペアードステートメントを使用した方が、安全なプログラムと言えるでしょう。
攻撃に対する防御は一つあれば十分ということはありません。その一つにに不具合があって、破られてしまったら終わりです。二重、三重の防御壁や、例え破られたとしても影響範囲を最小限に絞る仕組みを取り入れる、などと言ったことが必要です。ただ、中にはコストや手間がかかる物もあります。そこら辺は採用しなかった場合のデメリットとのトレードオフになります。しかし、プリペアードステートメントのような、コストも手間も必要がない物を採用しないという選択肢はありません。必要・不必要という判断では無く、可能な限りより安全な方を採用すべきです。
投稿2016/05/07 23:39
総合スコア21735
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
総合スコア2468
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
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
総合スコア11701
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/05/10 12:59
0
SQLで変数を使用する場合は、必ずプリペアードステートメントを使用すると考えていましたが、SQLインジェクションを受けないような箇所では、必要ないのでしょうか?
ご推察のとおりです。
何でもかんでもプリペアードステートメントで対応するというのは誤った考え方です。
安全のみならず安心も考えた対応が望ましいです。
運用で不具合が出た際、データの整合性・正当性のチェックのため、ログに吐かれたSQL文を
よく使うことになります。
この際、?は人力で変更を掛けて、DBにクエリを投げて確認するという手法になります。
1つや2つであれば、それほどの苦労ではないかもしれません。
しかし、一連のロジックの流れの中で投げているSQL文全部を調査するとなると、
sqlは10本を超え、?の読み替えは数十本になるという可能性は大いにあります。
これを緊急にやらないといけないことを考えれば、
「何でもかんでもプリペアードステートメント」
では、保守者の負担を増大させている事に気づけるかと思います。
それなので、SQLインジェクションの不安があるけど、使いたくない場合は、
チーム内で相談されて対応決めるのがベストかと思います。
投稿2016/05/08 05:51
総合スコア1124
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/05/08 11:09
0
私なら・・・・ですが、
- 今後の展開でrequestから値を入れることがあるかも
- prepared statmentで行う部分とそうでない部分が一つのシステムに混在するのはコーディング規約的に嫌
- prepared statmentでやる場合のデメリットが感じられない
以上の理由からprepared statmentで組み込みますね。
セキュリティ的なことを考えるなら、大丈夫だからと言って敢えてセキュリティ問題を起こしそうなロジックを入れるというのは最初に避けて通りますね。
備えあれば・・・の精神ですね。
投稿2016/05/08 01:26
総合スコア141
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/05/08 03:55
0
bind(プリペアードステートメントで変数を定義する)で、高速化できるメリットがあります。
一部のDBMS(Oracle,SQL server)では、SQLを実行する際にその実行計画(実行プラン)をキャッシュしてくれる機能があります。
例えば、1000レコードINSERTするような場合、同じSQLでINSERTする値を変数化すると、キャッシュが効いて、実行するSQLの解析コストを1000回から1回に減らすことができます。
MySQLにはこの実行計画のキャッシュ機能がないので、質問者のおっしゃる通り、POST/GETパラメータやCookie値以外に、変数化するメリットはあまり感じません。
投稿2016/05/07 23:25
総合スコア80
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/05/08 03:58
2016/05/10 10:41 編集
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/05/08 03:57