こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

SQLインジェクションの対策について

一般的に、プリペアードステートメントを使用することやSQLを別ファイルで管理することなど言われますが、なぜそれらが対策として有効なのでしょうか。
いろんなサイトを見ていますが、あまりピンと来ていません。。。
・プリペアードステートメントを使用すること
・別ファイルで管理すること
これらがなぜ有効なのかを教えていただけませんでしょうか。

投稿日時 - 2014-11-22 14:52:23

QNo.8833504

困ってます

質問者が選んだベストアンサー

 SQLインジェクションの発生原因から考えれば、この対策が何をしようとしているのか見えてくると思います。
 ちょっと復習しておきましょう。
 例えば、すっごく安直ですが・・・
 select name, age from tbla where age>****
 というSQLを発行する手続きがあったとします。
 ****の部分には、フォームの入力が来ます。つまり、ページの閲覧者が指定した年齢以上の人の一覧表を出そうとしているわけですね。
 さて、なんにも考えずに、普通に作ってみましょう。
 フォームからの入力は、変数
 string Form_age
 に入っているとします。
 string sql = "select name, age from tbla where age>" + Form_age;
 execute_sql(sql);

 なんてやったら、どうなるか。普通の入力が入ってくれば、何にも不都合はありません。正常にちゃんと機能します。
 問題は、悪意のある人がコレを使ったらどうなるかです。
 悪意のある人は、フォームにこんな入力をします。
 23;delete from daijinatable;
 さて、これが、Form_ageに入っている中で、先の文を実行すると、
 select name,age from tbla where age>23;delete from daijinatable;
 を実行することになります。
 SQLでは、;で文を区切ることにより、複数のSQLを一度に実行することが出来る場合が多いです。
 つまり、先の文だと、select と deleteの二つの文を一気に実行します。
 その後の結果表示は、deleteのせいでデタラメになるでしょうし、多分、プログラム側がエラーとなるでしょう。でも、悪意のある人にはそんなことはどうでもよいのです。彼が実行したかったのは、delete from daijinatableの方です。これは、ちゃんと無事に実行されます。その結果、あなたは、何日か後に、大事なテーブルのデータがいつの間にか消滅していることに気がつき悲鳴を上げることになります。
 
 これが、SQLインジェクションの基本です。
 何が原因でこんなことになったのか?form_ageの中には必ず数字を表す文字列が入っていると固く信じて、そのままSQL文の一部分にしたのが最大の原因です。
 
 根本的に、この問題を解決するためには、from_ageの中身をちゃんとまず検査することです。今回の場合であれば、form_ageの中に数字が入っていることを確認し、数字で無ければ、エラーページを出すということをちゃんとやっていれば良いわけです。
 このように、中身が数字とか、ある一定の規則に従った文字列・・・なら検査も簡単です。でも、これが、ワード検索の単語とかだと、何が入ってくるかは様々ですから、検査も大変です。
 代案を考えてみましょう。
 間違った文字が入っていることそのものは、システムやプログラムに対しては無害です。問題になるのは、SQL文にとって特別な意味を持っている一部の文字に対しての扱いです。文字列はちゃんと"か'で囲う。文字列中の"や'は、ちゃんとエスケープする。;等の文末文字が文字列の外に出ないようにする・・・等等等等の構文的な解釈を全部ちゃんと自分でチェックして、所用のエスケープ処理をしてから、SQL文の一部に結合すれば、システム的には問題なくなります。
 でも、これ、自分では書かないでください。間違いなく抜けが出ます。
 このようなことは、システムに任せましょう。システムにも時としてバグがありますが、まぁ、少なくともあなたが書くよりは抜けは少ないです。
 これをシステムに任せるために使うのがプリペアードSQLです。
 先の例だと、
 string sql = "select name,age from tbl1 where age > %age"
 のようにしておいて、システム関数で、%ageに対して、値を代入して、SQLを実行します。このとき、DBMSは、%ageに入ってる文字列は、いかなる場合でもageとの比較にしか使わない事を保証します。実装としては、%ageへの代入の際に徹底したエスケープ処理をしているかもしれませんし、もっと本質的に、DBMS側で、%ageの管理を別途行い、比較処理をしているかもしれません。それはシステムにより異なります。

 ちなみに、プリペアードSQLを使ったから、フォームに入力された値のチェックはやらなくていいよねとはしない方が良いです。外部からの入力に関しては、自分で簡単にできるチェックはちゃんと自分でやるのが基本です。自分でやれば、フォーム欄の意味に沿ったエラーページを出すことも出来ますし、ユーザーフレンドリーです。
 その上で、プリペアードSQLのようなシステム側の機能「も」利用する。そうすることにより、よりいっそう強固なシステムと出来ます。

投稿日時 - 2014-11-23 21:03:27

このQ&Aは役に立ちましたか?

7人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.2

>・プリペアードステートメントを使用すること

プリペアドステートメント自体が有効なわけではありません。
「プログラムで動的にSQLを作成しないこと」が重要(※)。
そうはいっても、VALUE() や WHERE などの部分を可変にしたいわけなので、
プリペアドステートメントを使うとプレースホルダの機能を使うことで、動的にSQL文をつくらずに済みます。
その意味を理解せず、「プリペアドステートメントを使えばいいんだ」と動的に組み立てたSQL文をPREPAREしたら全くの意味無しです。

>・別ファイルで管理すること

これはSQLインジェクションとは関係ないと思いますよ。
その観点では意味無しです。

※もしどうしても動的にSQL文を組み立てざるを得ないときは十分に注意して正しいやり方でする事。

投稿日時 - 2014-11-23 13:00:11

ANo.1

・プリペアードステートメントを使用すること
フォーム等から入力された文字列にSQL文上意味のある文字を入力されても、単なるデータの一部として処理されSQL文の一部と見なされないようになるため、SQLインジェクションから守ることが出来ます。

・別ファイルで管理
十分な対策とは言えない気もしますが、プログラム中でSQL文を組み立てる処理をしているとパラメータとして渡された文字列の影響で意図しないSQL文になってしまい、SQLインジェクションの餌食になる可能性があります。
予めSQL文を完成させておくことでSQLインジェクションから守る役割を果たすという事だと思います。

投稿日時 - 2014-11-22 15:24:02

あなたにオススメの質問