トップ回答者
SQLインジェクション対策についての質問です、特別な用語の入力禁止(例:「;」)について。

質問
-
SQLインジェクション対策について:
1.パラメタライズドクエリ使用
2.コンボボックス--指定外の入力は禁止しておく。
3.テキストボックスへの入力制限は、以下のようにする。
①数値専用には、文字入力禁止
②入力文字数(桁数)の制限上記のように対策をしています。
これ以外に対策をした方が良いでしょうか?
(サーバーでの対策は別ですから、ここでは考えません)論理語というのでしょうか? 正確な専門用語は知りませんが、良く目にする表現があります。
よく見かけますし、ファイル名の変更時にも見かける表現です。”「・(なかぐろ),*,$,%,&,+,;,@,=」は、使用できません。”というような、表現です。
質問1--これも制限すべきでしょうか(全部、または、この一部)?
質問2--しかし、上記の一部の使用は避けられない場合もあります。
この場合、一部は、ひらがな入力でも表示します。
上記のものは、ひらがな入力すると、別扱いとなりますか(要するに危険でなくなりますか)?
「;」が危険なのは分かりますが、他はどうなのでしょうか?パラメタライズドクエリでエスケープしてあるので、不要と言えばそれまでですが?
YKsaila
回答
-
SQLインジェクションへの対策としては、パラメタライズドクエリの使用の他に、データ型を正しく扱うことでしょうか。本来数値であるべきところを文字列としてSQL Serverに渡すと、SQL Server側で変換することになるため問題が発生し得ます。
「数値専用には、文字入力禁止」等が挙げられていますが、プログラム内でデータ型変更していれば当然、数値に変換する際にエラーが発生しSQLを呼び出す前に止まります。ですからこの対策はプログラムとしては取るべき措置ですが別段SQLインジェクション対策には含まれません。後半はSQLインジェクション対策と無関係な質問と思いますので、スルーします。関連があるならその説明を、無関係なら質問を分けてください。
- 回答としてマーク yksaila 2012年12月6日 1:44
-
パラメタライズドクエリでエスケープしてあるので、不要と言えばそれまでですが?
その通りで、SQLインジェクションに関してはそれで十分です。SQLインジェクション対策は自分で行うのではなく、パラメタライズドクエリのようなバインドメカニズに任せた方が間違いがありませんし、楽です。
(参考)
SQL インジェクション
http://msdn.microsoft.com/ja-jp/library/aa302430#ID0EBEAC上記にも書いてありますが、バインドメカニズはSQLインジェクションに関するエスケープは行いますが、Like句におけるエスケープは行いませんので注意して下さい。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2012年12月4日 5:23 URL修正
- 回答としてマーク yksaila 2012年12月6日 1:44
-
設計をきちんとしていれば大丈夫です。
インジェクション系の攻撃は、特殊な意味を持つ文字を挿入(インジェクション)することで可能になります。従って、「特殊な文字とは何か」を把握し、「特殊な文字を無害化する手段」を執ることで防ぐことができます。一昔前なら「入力でサニタイズ」といわれていましたが、これは有効な対策ではありません。「境界で、移動先にあわせて無害化」をして下さい。
この考え方で行くと、「コンボボックス--指定外の入力は禁止しておく。」「テキストボックスへの入力制限は、以下のようにする。」は、SQL インジェクション対策ではありません。アプリケーションとしての入力制限/入力検査の範囲です。それらはアプリケーションの設計として必要なものです。また、SQL インジェクション対策として挙げられていないものとしては、権限の分化でしょうか。テーブルを作成、削除、閲覧する等の権限について、アプリケーションにアクセスするユーザを考えて適切に付与します。もし、無害化が十分でなくても、被害を小さく抑えることが出来ます。
また、もし、作成されているのがウェブ アプリケーションであるなら、SQL インジェクションへの対策だけでは不十分で、スクリプト インジェクションに対しても対策が必要です。Windows アプリケーションであっても、データを移動させる境界で、移動先に対して必要な無害化を行います。
なお、';'を入力無効とするのは、聞いたことがありません。「INSERT INTO TABLE ('; SELECT * FROM IMPORTANTTABLE')」のような文が実行されても「SELECT * FROM IMPORTANTTABLE」が実行される事はなく、「; SELECT * FROM IMPORTANTTABLE」という値が登録されるだけです。'''(引用符)を無害化せずに放っておいた場合、「INSERT INTO TABLE (''); SELECT * FROM IMPORTANTTABLE;--')」のような文が実行される事になったとき、危険があります。
'*'や'?'に入力制限があるのは、検索を行うときではないでしょうか。逆に、検索対象とならない列であれば、そのまま入力しても良いのではないでしょうか。その他、文字コードの扱いが正しくない場合に、'・'(中点)が正しく処理されないことがあったような?これらも SQL インジェクションへの対策ではありません。
Jitta@わんくま同盟
-
Like句は、時々使用します。 %や*を使います。 下記引用文のようにすれば良いのですね。
下記のs = s.Replace("%", "[%]")は、SQlparameters.addwithvalue文の後に書けばよいのですか?
いえ、パラメータに渡す前です。前述した通り、バインドメカニズムによって%などはエスケープされません。したがって、%をそのまま渡すと、Like句における%は特別な意味を持ってしまいます。よって、%は自分でエスケープしてからパラメータに渡す必要があります。
例を挙げましょう。
仮に取引先名が「hoge%」という会社があったとしましょう。これを検索するためにLike句に適応されるパラメータとして「hoge%」として渡すと、hogeで始まる取引先全てが検索されてしまいます。そうではなく、hoge%に完全一致する取引先を検索するために、hoge[%]と%をエスケープして渡す必要があります。まとめます。バインドメカニズムがエスケープを自働で行うのは、SQLインジェクションに関する部分についてです。その他は必要に応じてエスケープする必要があります。hoge%として渡したい時もありますし、hoge[%]として渡したい時もあります。よって、これらついてはバインドメカニズムは自動的にエスケープしないのだと思います。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク yksaila 2012年12月6日 1:45
-
SQL Server の場合は、常に静的プレースホルダが使用されるため、プレースホルダを利用している限り SQL インジェクションの問題はないはずです。詳しくは下記の pdf ファイルの記事が参考になると思います。
安全なSQLの呼び出し方 - IPA 独立行政法人 情報処理推進機構
http://www.ipa.go.jp/security/vuln/documents/website_security_sql.pdf
#LIKE 句の話は SQL インジェクションとは別の問題かと思いますが。- 回答としてマーク yksaila 2012年12月6日 1:45
すべての返信
-
SQLインジェクションへの対策としては、パラメタライズドクエリの使用の他に、データ型を正しく扱うことでしょうか。本来数値であるべきところを文字列としてSQL Serverに渡すと、SQL Server側で変換することになるため問題が発生し得ます。
「数値専用には、文字入力禁止」等が挙げられていますが、プログラム内でデータ型変更していれば当然、数値に変換する際にエラーが発生しSQLを呼び出す前に止まります。ですからこの対策はプログラムとしては取るべき措置ですが別段SQLインジェクション対策には含まれません。後半はSQLインジェクション対策と無関係な質問と思いますので、スルーします。関連があるならその説明を、無関係なら質問を分けてください。
- 回答としてマーク yksaila 2012年12月6日 1:44
-
パラメタライズドクエリでエスケープしてあるので、不要と言えばそれまでですが?
その通りで、SQLインジェクションに関してはそれで十分です。SQLインジェクション対策は自分で行うのではなく、パラメタライズドクエリのようなバインドメカニズに任せた方が間違いがありませんし、楽です。
(参考)
SQL インジェクション
http://msdn.microsoft.com/ja-jp/library/aa302430#ID0EBEAC上記にも書いてありますが、バインドメカニズはSQLインジェクションに関するエスケープは行いますが、Like句におけるエスケープは行いませんので注意して下さい。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2012年12月4日 5:23 URL修正
- 回答としてマーク yksaila 2012年12月6日 1:44
-
trapemiyaさんへ
Like句は、時々使用します。 %や*を使います。 下記引用文のようにすれば良いのですね。
下記のs = s.Replace("%", "[%]")は、SQlparameters.addwithvalue文の後に書けばよいのですか?
YKsaila
以下、引用文です。
LIKE 句を使用する
LIKE 句を使用している場合には、ワイルドカード文字にもエスケープ文字の追加が必要になることに注意してください。次のコードは、このような場合に適した手法の例を示しています。
s = s.Replace("[", "[[]"); s = s.Replace("%", "[%]"); s = s.Replace("_", "[_]");
-
設計をきちんとしていれば大丈夫です。
インジェクション系の攻撃は、特殊な意味を持つ文字を挿入(インジェクション)することで可能になります。従って、「特殊な文字とは何か」を把握し、「特殊な文字を無害化する手段」を執ることで防ぐことができます。一昔前なら「入力でサニタイズ」といわれていましたが、これは有効な対策ではありません。「境界で、移動先にあわせて無害化」をして下さい。
この考え方で行くと、「コンボボックス--指定外の入力は禁止しておく。」「テキストボックスへの入力制限は、以下のようにする。」は、SQL インジェクション対策ではありません。アプリケーションとしての入力制限/入力検査の範囲です。それらはアプリケーションの設計として必要なものです。また、SQL インジェクション対策として挙げられていないものとしては、権限の分化でしょうか。テーブルを作成、削除、閲覧する等の権限について、アプリケーションにアクセスするユーザを考えて適切に付与します。もし、無害化が十分でなくても、被害を小さく抑えることが出来ます。
また、もし、作成されているのがウェブ アプリケーションであるなら、SQL インジェクションへの対策だけでは不十分で、スクリプト インジェクションに対しても対策が必要です。Windows アプリケーションであっても、データを移動させる境界で、移動先に対して必要な無害化を行います。
なお、';'を入力無効とするのは、聞いたことがありません。「INSERT INTO TABLE ('; SELECT * FROM IMPORTANTTABLE')」のような文が実行されても「SELECT * FROM IMPORTANTTABLE」が実行される事はなく、「; SELECT * FROM IMPORTANTTABLE」という値が登録されるだけです。'''(引用符)を無害化せずに放っておいた場合、「INSERT INTO TABLE (''); SELECT * FROM IMPORTANTTABLE;--')」のような文が実行される事になったとき、危険があります。
'*'や'?'に入力制限があるのは、検索を行うときではないでしょうか。逆に、検索対象とならない列であれば、そのまま入力しても良いのではないでしょうか。その他、文字コードの扱いが正しくない場合に、'・'(中点)が正しく処理されないことがあったような?これらも SQL インジェクションへの対策ではありません。
Jitta@わんくま同盟
-
Like句は、時々使用します。 %や*を使います。 下記引用文のようにすれば良いのですね。
下記のs = s.Replace("%", "[%]")は、SQlparameters.addwithvalue文の後に書けばよいのですか?
いえ、パラメータに渡す前です。前述した通り、バインドメカニズムによって%などはエスケープされません。したがって、%をそのまま渡すと、Like句における%は特別な意味を持ってしまいます。よって、%は自分でエスケープしてからパラメータに渡す必要があります。
例を挙げましょう。
仮に取引先名が「hoge%」という会社があったとしましょう。これを検索するためにLike句に適応されるパラメータとして「hoge%」として渡すと、hogeで始まる取引先全てが検索されてしまいます。そうではなく、hoge%に完全一致する取引先を検索するために、hoge[%]と%をエスケープして渡す必要があります。まとめます。バインドメカニズムがエスケープを自働で行うのは、SQLインジェクションに関する部分についてです。その他は必要に応じてエスケープする必要があります。hoge%として渡したい時もありますし、hoge[%]として渡したい時もあります。よって、これらついてはバインドメカニズムは自動的にエスケープしないのだと思います。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク yksaila 2012年12月6日 1:45
-
SQL Server の場合は、常に静的プレースホルダが使用されるため、プレースホルダを利用している限り SQL インジェクションの問題はないはずです。詳しくは下記の pdf ファイルの記事が参考になると思います。
安全なSQLの呼び出し方 - IPA 独立行政法人 情報処理推進機構
http://www.ipa.go.jp/security/vuln/documents/website_security_sql.pdf
#LIKE 句の話は SQL インジェクションとは別の問題かと思いますが。- 回答としてマーク yksaila 2012年12月6日 1:45
-
SQL Server の場合は、常に静的プレースホルダが使用されるため、
この部分の意図するところがわかりませんでした。静的プレースホルダであるか動的プレースホルダであるかは、アプリケーション側が決めることではないでしょうか? SQL Serverは静的プレースホルダに対応しており、アプリケーションからは静的プレースホルダの形式でSQL ServerにSQL文を送信することもできるという表現が適切に思いますが、いかがでしょうか?
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
-
-
pdf文書は読んでいます。
静的プレースフォルダ:データベースエンジン側がバインド処理
動的プレースフォルダ:バインド処理をアプリケーション側のライブラリ内で実行
これはデータベース一般について書かれたものであり、SQL Serverについて書かれたものではありません。
ということで、「SQL Server の場合は」という表現で良いと思っています。
よって、なぜ「ということで」につながるのか、はっきりとはわかりませんでした。ただ、後述しますが、想像はしています。
さて、パラメタライズドクエリを静的プレースホルダの形式でSQL文を発行するか、動的プレースホルダとして処理してからSQL文を発行するかはライブラリ側の問題であり、データベース側で決定できることではありません。
ですから、「SQL Server の場合は、常に静的プレースホルダが使用されるため」が唐突に現れたことに違和感を覚え、参考として示されていたpdf文書もデータベース一般について書かれたもの、言うなれば用語解説的なものだったからです。以下、私の想像になりますが、このようなことを言われたいのでしょうか?
「SQL Serverに対してADO.NETというライブラリを使用した場合、パラメタライズドクエリを使用すると、静的プレースホルダで処理されるようにSQL ServerにSQL文が発行される。」
これであれば理解できます。ただ、ADO.NETはSQL Serverに対してパラメタライズドクエリを使用した場合、常に静的プレースホルダを使用するということが書かれた文献を探せませんでしたので、どこからこの情報を得られたのか、問題なければ教えていただければうれしく思います。
失礼ながら、結局のところSurferOnWwwさんの表現が、はしょりすぎていただけかな?と、今は思っています。ただ、はしょりすぎると特に初学者の方には誤解を生みやすいと思いましたので、モデレータとしての立場もあり突込みを入れていますので、この辺りご了解いただけると幸いです。もちろん、私の至らないところを忌憚なく指摘していただくのは大歓迎です。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
-
SurferOnWwwさんへ
横から失礼します。 お二人の会話は、大変役に立っています。
このフォーラム(掲示版)を利用し始めたころ、どなたかに例のPDF文書を紹介していただき、私も一通り読みました。
もちろん、保存してあります。 初めて接する話題でした。 SQLインジェクションなる言葉も、ここで初めて知りました。
静的プレースフォルダー、動的プレスフォルダーも、完全には理解出来ていないのでは? と、不安に思っています。
要するに、”初学者の私たちには” 大いに役立ちますので、是非この議論を続けてください。 できれば、細かいことについても(お二人には、自明のことでも)省かずに、お話を続けていただければ、ありがたいです。
YKsaila
-
trapemiya さん>
話が噛み合ってないようですね。
> なぜ「ということで」につながるのか、はっきりとはわかりませんでした。
SQL Server でも、SqlClient を使わないで、データプロバイダーを自作するなどして動的プレースフォルダーを使うことは可能かもしれませんが、現実的にあり得ないと思います。あり得ない話をする必要はないと思います。
さらに、SQL インジェクション対策の話をしていて、パラメータ化するのが前提なので、自分は先のレス「SQL Server の場合は、常に静的プレースホルダが使用されるため、プレースホルダを利用している限り SQL インジェクションの問題はないはずです。」には 1 ミリも違和感は感じてません。
> ADO.NETはSQL Serverに対してパラメタライズドクエリを使用した
> 場合、常に静的プレースホルダを使用するということが書かれた文
> 献を探せませんでしたので、どこからこの情報を得られたのか、問
> 題なければ教えていただければうれしく思います。紹介した pdf 文書に書いてあります。Microsoft の文書であれば、以下はいかがでしょう?
パラメーターおよびパラメーターのデータ型の構成 (ADO.NET)
http://msdn.microsoft.com/ja-jp/library/yy6y35y8%28v=vs.100%29.aspxSQL Server 2005 Tips and Tips
http://technet.microsoft.com/ja-jp/sqlserver/gg639072前者のページの "パラメーター化ステートメントは、sp_executesql を使ってサーバー上で実行されるため、クエリプランの再利用が可能になります。" と、後者のページの "SqlParameter クラスを利用すると、内部的には sp_executesql に変換されて実行されるようになります。" あたりがポイントかと思います。
> はしょりすぎると特に初学者の方には誤解を生みやすいと思いましたので、ADO.NET の SqlClient を使うのはデフォルトでしょうから、はしょっていいと思います。相手が初学者の場合は、特に、情報が多すぎるとオーバーフローすることがあると思います。
-
SurferOnWwwさん>
>SQL Server でも、SqlClient を使わないで、データプロバイダーを自作するなどして
>動的プレースフォルダーを使うことは可能かもしれませんが、現実的にあり得ないと思います。
>あり得ない話をする必要はないと思います。あり得ない話なのか、自作しなくてもそのようなプロバイダーが存在するのか?あるいは、.NET以外のプラットフォームではどうなのか? それは全て調査したわけではありませんので、私にはわかりません。
ただ私が違和感を覚えたと書いたのは、最初のSurferOnWwwさんへの問いかけで書いている通り、表現についてなのです。初学者に対してどう表現するかは、それぞれのポリシーがあると思いますし、相手やその時の状況によると思いますので、ここはSurferOnWwwさんと私の溝が埋まらない部分かもしれません。
また、私が違和感という表現を使ったのがいけなかったのかもしれません。違和感というよりは、誤解を招く表現だと感じたという方が近いと思います。具体的には唐突に「常に」という言葉が出てきた部分です。
結局のところ、SurferOnWwwさんの前提としてADO.NETがあり、私はもう少し広範囲に考えていたという違いだと思います。
SurferOnWwwさんが言われるように、ADO.NETが前提に決まっていると考えられる人もいると思いますし、私と同じように考える人もいると思いました。また、言葉尻だけから誤解される人もいると思いました。リンク先のpdfはいつまでもある保障はありませんし、リンク切れになった場合、特に心配です。SurferOnWwwさんの意図がうまく伝わらず、仮に動的プレースホルダを使うデータプロバイダが存在していた場合(あり得ないかもしれませんが、0とは言い切れないと思います)、SQLインジェクションを起こしてしまう人がいるかもしれません。ですから、もう少し誤解の無い表現の方がいいのではないかと思いました。正直、重箱の隅を突くような感覚や考えすぎという感覚もあり、SurferOnWwwさんへのレスをしようか迷ったのですが、いろいろ考えた末、頭の片隅にモデレータとして、より有意義な回答を残していきたいという思いが勝ちました。
いずれにしても、SurferOnWwwさんのおかげで、確実に良い情報が付加されたと思っています。SurferOnWwwさんとはMSDNフォーラムだけでのお付き合いですが、SurferOnWwwさんの技術力が高いことは十分に存じておりますし、私も少なからず勉強させていただいています。ご紹介いただいたマイクロソフトの文書も、大変役に立ちました。ありがとうございます。参考になったとして一票いれさせていただきました。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2012年12月8日 14:22 改行位置等修正
-
trapemiya さん>
パラメータ化とエスケープのあたりに理解の違いがあるのが違和感の原因ではないかと思われます。
以前のレスで、以下のように述べられていますが、パラメータ化するとエスケープ処理されると言われてますか?
> SQL インジェクション
> http://msdn.microsoft.com/ja-jp/library/aa302430#ID0EBEAC
>
> 上記にも書いてありますが、バインドメカニズはSQLインジェクション
> に関するエスケープは行いますが、Like句におけるエスケープは行いませ
> んので注意して下さい。とすると、少なくとも SQL Server の場合はそれは誤解だと思います(自分が調べた限りですが、エスケープ処置はされません。そもそも静的プレースホルダ方式なので処置する必要がありません)。
そう書くとまた違和感を感じられるかもしれませんが、上の URL のページにもありますように、"Parameters コレクションを使用すると、入力はリテラル値として処理されます。SQL 上、その入力が実行可能なコードとして処理されることはありません。" ということが前提で、そもそもエスケープする必要がないはずです。
前にも書きましたが、LIKE 句の話は SQL インジェクションとは別の問題です。(パラメータ化する/しないに関係なく、リテラルの一部としてワイルドカード文字を使用する場合はカッコで囲む必要があるということ)
SQL インジェクション防止に関してどこまで抜かりなくレスするかは難しいですね。細かいことを言われると、以下のページなどを読んでくださいとレスするぐらいしかなくなります。"高いスキルを持つ決然たる攻撃者は、パラメータ化されたデータであっても操作できるのです。" ということだそうですから。
SQL インジェクション
http://msdn.microsoft.com/ja-jp/library/ms161953(v=sql.100).aspxでも、そのページを紹介しただけでは、読まないもしくは読んでもわからないということになると思います。
- 編集済み SurferOnWww 2012年12月9日 2:37 誤記訂正
-
SurferOnWwwさんへ>
私がバインドメカニズムという言葉を使った元の資料は、以下のページの、
「SQL-Injection Defense Tree公開 (2005/9)wg-eval-sql200509[PDF]」になります。「バインドメカニズム」でこのPDF文書を検索してみて下さい。この文書は、特定の技術や製品を意識してバインドメカニズムという言葉を使っていませんが、私も同様です。Web Application Security Forum 公開資料
http://wasforum.jp/about-wasforum/publications/私はバインドメカニズムという言葉を使いましたが、この時、動的プレースホルダや静的プレースホルダについて全く意識しておらず、yksailaさんが自分でSQLインジェクション対策を行うのではなく、既に用意されている仕組みを使った方が良いという意味で使いました。おそらく、SurferOnWwwさんは動的プレースホルダや静的プレースホルダを意識されており、私は意識していなかったのが噛み合わない原因なのかもしれませんね。
LIKE句に関しては私もSQLインジェクションとは別物だと思っています。私がLIKE句を持ち出したのは、上に挙げたpdfだけではなく、SQLインジェクションを解説したページに、LIKE句についての注意点が割とよくセットで掲載されていたため、私も注意の意味で掲載しました。実際、どこかの掲示板でもSQLインジェクションに関する処理でLIKE句の扱いに関する質問を見たことがあります。おそらく、その質問は、バインドメカニズムがメタ文字(SQLの実行に意味を持つ文字)は全てエスケープしてしまうという勘違いから来ていたのかもしれません。
>"高いスキルを持つ決然たる攻撃者は、パラメータ化されたデータであっても操作できるのです。"
> ということだそうですから。これはまた衝撃的ですね。初めて知りました。一般の技術者はここまで意識する必要はないとは思いますが・・・
それで思い出しました。
話が少し変わりますが、静的プレースホルダの概念を知らないと、SQLインジェクション対策をしている気になったまま実はしていないということがあります。これは、SurferOnWwwさんへではなく、一般のみなさんに対して書き込むのですが、ADO.NETは静的プレースホルダでパラメータをSQL Serverに渡すため、そのパラメータを使ってストアドプロシージャ内でSQL文を文字列連結で組み立てては何もならないからです。ストアドプロシージャ内でちゃんとバインドメカニズムを使う必要があります。★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
-
>一般のみなさんに対して書き込むのですが、ADO.NETは静的プレースホルダでパラメータをSQL Serverに渡すため、
>そのパラメータを使ってストアドプロシージャ内でSQL文を文字列連結で組み立てては何もならないからです。
>ストアドプロシージャ内でちゃんとバインドメカニズムを使う必要があります。要するに、パラメタライズドクエリをそのままストアドプロシージャ内に書くのは、無意味・本末転倒で危険だということですね。
余談ですが、ネットワークの専門家(一流)に聞いたことがあるのですが、不可能なことは少ない、そうです。 本当なんでしょうか(雑談ですから、ただの話として読んでください)。
クライアントにもセキュリティの穴が存在し、そこからサーバーに侵入される。 穴は初めはなくても、使用の中で発生する、使用者が不用意だと。サーバーへの侵入も程度があるらしいですが、サーバーを完全に乗っ取られたら、なす術ははないとか? 最近の国会でのデータ漏えい事件がそうらしいです。 担当会社が、サーバーの防御を怠ったか、無能だったか、どちらからしいです。 サーバーへの侵入防御対策を、きちんとしておくのが肝要らしいです。 通常の会社は、そういう超一流の攻撃者の対象にはならないから、大丈夫だけど。と、言っていました。
難しいものですね。
YKsaila
-
少し、読んでみました。 ほんの少し理解しました、完全ではないですが。
それで、ですが:
「;」 「'」 「--」 「xp_」 は、入力禁止にした方がいいのですね。
それと、「/*---*/」ですが、これは、「/*」 「*/」を単独で入力禁止にすればいいのですか? それとも、、「/*---*/」ですか?
前者ですよね。
また、S=S.Replace("[","[[]")、S=S.Replace("]","[]]")、S=S.Replace("%","[%]")、S=S.Replace("_","[_]")
をやっておけばいいのですね?
以前、書き込みで、($ & + ; @ = %)を入力禁止にしてみたのですが、そうではなく、「%」はエスケープ、「;」は入力禁止にすればいいのであって、、($ & + @ =)の入力禁止は不要で、いいでしょうか?
YKsaila
-
-
>「LIKE 句の話は SQL インジェクションとは別の問題です。」と言ったのに、依然として-----
”LIKE 句の話はSQL インジェクションとは別の問題” ということは、だいぶ理解したつもりなのですが?
”> また、S=S.Replace("[","[[]")、S=S.Replace("]","[]]")---” の書き込みは、ついでとして書き込んだだけです(質問として)。
ただ、理解が不十分というのは、SurferOnWwwさんのおっしゃる通りです。
完全に理解していたら、上記の質問はしない、ということですか。
”LIKE 句の話が、< なぜ>SQL インジェクションとは別の問題か?”、ということであれば、私はまだ理解していませんし、この問題には答えられません---、という意味であれば、そうですね。
YKsaila
- 編集済み yksaila 2012年12月14日 1:17
-
要するに、パラメタライズドクエリをそのままストアドプロシージャ内に書くのは、無意味・本末転倒で危険だということですね。
誤解があってはいけませんので、確認のために例をあげましょう。会員情報というテーブルがあり、3件のレコードがあるとします。
1> select * from dbo.会員情報
2> go
userid password クレジットカード番号
--------------------------------------------------
hoge abc 1234567
moge xyz 7654321
fuga efg 567123(3 行処理されました)
ここから、会員のクレジットカード番号を読みだすプログラムを以下のように書きました。わざとSQLインジェクションが発生するようにパラメータの値を設定しています。
Dim dt = New DataTable() Using con As New SqlConnection(My.MySettings.Default.testdbConnectionString) Using cmd As New SqlCommand With cmd .Connection = con .CommandType = CommandType.StoredProcedure .CommandText = "dbo.Get会員情報" .Parameters.AddWithValue("@userid", "moge") .Parameters.AddWithValue("@password", "xyz' or 'A' = 'A") End With Using da As New SqlDataAdapter da.SelectCommand = cmd da.Fill(dt) End Using DataGridView1.DataSource = dt End Using End Using
以下に、SQLインジェクションの発生するパターンを含めて、Get会員情報というストアドプロシージャを示します。
CREATE PROCEDURE dbo.Get会員情報 @userid nvarchar(50) = '', @password nvarchar(50) = '' AS BEGIN --■パターン1。このパターンはSQLインジェクションは発生しない。バインドメカニズムが使用されるため。 --SELECT userid, password, クレジットカード番号 -- from dbo.会員情報 -- where userid = @userid and password = @password --■パターン2。このパターンもSQLインジェクションは発生しない。バインドメカニズムが使用されるため。 --declare @sqlwk nvarchar(500) = '' --set @sqlwk = 'select userid, password, クレジットカード番号 from dbo.会員情報' + -- ' where userid = @userid and password =@password' --exec sp_executesql @sqlwk, N'@userid nvarchar(50), @password nvarchar(50)', @userid, @password --■パターン3。このパターンはSQLインジェクションが発生する。バインドメカニズムを使用しないため。 declare @sqlwk nvarchar(500) = '' set @sqlwk = 'select userid, password, クレジットカード番号 from dbo.会員情報' + ' where userid = ''' + @userid +''' and password = ''' + @password + '''' exec sp_executesql @sqlwk END
このような単純な例ではパターン3を書く人はまずいないでしょうが、複雑な検索条件を指定したりする場合など、パターン3で書いてしまう人がいます。これは、パラメタライズドクエリを使っており、ストアドプロシージャで処理までしているからSQLインジェクションは発生しないという思い込みから来ています。ストアドプロシージャを使う場合は、バインドメカニズムはデータベース側、すなわちストアドプロシージャで行うということを頭にたたきいれて下さい。
ちなみに、ストアドプロシージャを使わずに、VB上でselect文を書いて発行する場合、それがパラメタライズドクエリとして書かれていれば、常に上記ストアドプロシージャ内のパターン2の構文に変換されてからSQL Serverに送られますので、やはりデータベース側でバインドメカニズムが動作し、SQLインジェクションが防止されます。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2012年12月14日 2:20 加筆
-
「;」 「'」 「--」 「xp_」 は、入力禁止にした方がいいのですね。
絶対にそのような文字、または文字列が出てこないのであれば入力不可にしても良いと思いますが、例えば「'99年」などと入力することはないのでしょうか?メタ文字はデータベースによって変わりますし、本来はデータベース側できちんとSQLインジェクションを防止する仕組みを使うのが基本であり、まずそれで通常は問題ないはずです。その上で、入力不可である文字、または文字列を設定すればさらにセキュリティは上がると思いますが、よく言われるようにセキュリティと利便性は相反しますので、その辺りで判断することになると思います。
また、S=S.Replace("[","[[]")、S=S.Replace("]","[]]")、S=S.Replace("%","[%]")、S=S.Replace("_","[_]")
をやっておけばいいのですね?
一概には言えません。例えば%を含む文字列を探したい場合は%を[%]とエスケープしてからSQL Serverに送らなければなりませんし、%をメタ文字として使用したい場合(この場合の%は任意の数の文字と一致という意味になる)は、そのまま%をSQL Serverに送らなければなりません。
と、書いたところまでが基本です。しかし、実際に私が行うのは常に%を[%]とエスケープしています。なぜなら、ユーザーにメタ文字を意識させて入力させることを好まないからです。ユーザーが例えば「元気100%」という文字列を検索したい場合、元気100で始まる全ての文字列を検索したいと思うことは少ないでしょう。それとは別に語頭一致、完全一致、語尾一致というラジオボタンを設け、ユーザーの選択によって、%を付加するようにしています。例えば、ユーザーが「元気100%」と入力し、語頭一致を選択したのであれば、最終的に以下の文字列に変換して、SQL Serverに送ります。
元気100[%]%
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2012年12月14日 4:28 誤字修正