148

ユーザー入力を含むクエリを作成するときに、パラメーター化された SQL クエリがユーザー入力をサニタイズする最適な方法であることは認識していますが、ユーザー入力を取得して一重引用符をエスケープし、文字列全体を一重引用符で囲むことの何が問題なのか疑問に思っています。コードは次のとおりです。

sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"

ユーザーが入力した一重引用符は二重の一重引用符に置き換えられ、ユーザーが文字列を終了する機能を排除するため、セミコロン、パーセント記号など、ユーザーが入力する可能性のあるものはすべて文字列の一部になり、コマンドの一部として実際に実行されるわけではありません。

Microsoft SQL Server 2000 を使用していますが、単一引用符が唯一の文字列区切り文字であり、文字列区切り文字をエスケープする唯一の方法であると考えているため、ユーザーが入力したものを実行する方法はありません。

これに対して SQL インジェクション攻撃を開始する方法は見当たらないが、もしこれが私に思えるほど防弾であるなら、他の誰かがすでに考えていたであろうし、それは一般的な慣行であろう.

このコードの何が問題になっていますか? このサニタイズ手法を通過して SQL インジェクション攻撃を受ける方法はありますか? この手法を利用するユーザー入力のサンプルは非常に役立ちます。


アップデート:

このコードに対して SQL インジェクション攻撃を効果的に開始する方法はまだわかりません。バックスラッシュで 1 つの単一引用符をエスケープし、もう 1 つの引用符を残して文字列を終了し、残りの文字列が SQL コマンドの一部として実行されるようにすることを何人かの人々が提案しました。 MySQL データベースですが、SQL Server 2000 で単一引用符をエスケープする (私が見つけた) 唯一の方法は、別の単一引用符を使用することです。バックスラッシュはそれをしません。

また、一重引用符のエスケープを停止する方法がない限り、残りのユーザー入力はすべて 1 つの連続した文字列として処理されるため、実行されません。

入力をサニタイズするためのより良い方法があることは理解していますが、上記で提供した方法が機能しない理由を知りたいと思っています。このサニタイズ方法に対して SQL インジェクション攻撃を開始する特定の方法を誰かが知っている場合は、ぜひご覧ください。

4

18 に答える 18

91

まず第一に、それは悪い習慣です。入力の検証は常に必要ですが、常に不安定でもあります。
さらに悪いことに、ブラックリストの検証には常に問題があり、受け入れる値/形式を明示的かつ厳密に定義する方がはるかに優れています。確かに、これは常に可能であるとは限りませんが、ある程度は常に実行する必要があります。
この件に関するいくつかの研究論文:

ポイントは、あなたが行うブラックリスト(および許容度が高すぎるホワイトリスト)はバイパスできるということです. 私の論文への最後のリンクは、引用符のエスケープさえ回避できる状況を示しています。

これらの状況があなたに当てはまらない場合でも、それは悪い考えです。さらに、アプリが非常に小さい場合を除き、メンテナンスに対処する必要があり、場合によってはある程度のガバナンスも必要になります。いつでもどこでも適切に行われるようにするにはどうすればよいでしょうか?

それを行う適切な方法:

  • ホワイトリストの検証: タイプ、長さ、形式、または受け入れられる値
  • ブラックリストに登録したい場合は、すぐに行ってください。引用エスケープは良いことですが、他の軽減策との関連で。
  • Command オブジェクトと Parameter オブジェクトを使用して、事前解析と検証を行う
  • パラメーター化されたクエリのみを呼び出します。
  • さらに良いのは、ストアド プロシージャのみを使用することです。
  • 動的 SQL の使用を避け、文字列連結を使用してクエリを作成しないでください。
  • SP を使用している場合は、データベースの権限を、必要な SP のみを実行するように制限し、テーブルに直接アクセスしないようにすることもできます。
  • また、コードベース全体が SP を介してのみ DB にアクセスすることを簡単に確認することもできます...
于 2008-09-26T14:18:45.813 に答える
28

一言で言えば、自分でクエリをエスケープしないでください。あなたは何か間違っているに違いありません。代わりに、パラメーター化されたクエリを使用するか、何らかの理由でそれができない場合は、これを行う既存のライブラリを使用してください。自分でやる理由がない。

于 2008-09-26T12:51:17.427 に答える
21

質問されてからかなり時間が経っていることは承知していますが、..

「引数を引用する」手順に対する攻撃を開始する 1 つの方法は、文字列の切り捨てです。MSDN によると、SQL Server 2000 SP4 (および SQL Server 2005 SP1) では、長すぎる文字列は静かに切り捨てられます。

文字列を引用すると、文字列のサイズが大きくなります。すべてのアポストロフィが繰り返されます。これを使用して、SQL の一部をバッファの外にプッシュできます。そのため、where 句の一部を効果的に削除できます。

これはおそらく、「update」ステートメントを悪用して、実行するはずのすべてのチェックを実行しない可能性がある「ユーザー管理」ページのシナリオで最も役立ちます。

そのため、すべての引数を引用符で囲むことにした場合は、文字列のサイズがどうなっているのかを確認し、切り捨てが発生しないように注意してください。

パラメータを使用することをお勧めします。いつも。データベースでそれを強制できればいいのにと思います。また、副作用として、より多くのステートメントが同じように見えるため、より良いキャッシュ ヒットが得られる可能性が高くなります。(これは確かに Oracle 8 に当てはまりました)

于 2009-02-19T10:57:55.770 に答える
11

私は、「高度な検索」機能を扱うときにこの手法を使用しました。そこでは、最初からクエリを作成することが唯一の実行可能な答えでした。(例: ユーザーが製品属性に関する無制限の制約セットに基づいて製品を検索できるようにし、列とそれらの許可された値を GUI コントロールとして表示して、ユーザーの学習しきい値を減らします。)

それ自体は安全です。ただし、別の回答者が指摘したように、バックスペースのエスケープにも対処する必要がある場合があります (少なくとも、ADO または ADO.NET を使用して SQL Server にクエリを渡す場合ではありませんが、すべてのデータベースまたはテクノロジを保証することはできません)。

問題は、どの文字列にユーザー入力が含まれているか (常に悪意がある可能性があります)、どの文字列が有効な SQL クエリであるかを確認する必要があることです。落とし穴の 1 つは、データベースの値を使用する場合です。これらの値はもともとユーザーが提供したものでしたか? その場合、それらもエスケープする必要があります。私の答えは、SQL クエリを作成するときに、できるだけ遅く (ただし、遅くはしないでください!) サニタイズを試みることです。

ただし、ほとんどの場合、パラメーター バインドが適しています。単純な方法です。

于 2008-09-26T12:51:34.853 に答える
9

入力衛生は、中途半端にしたいものではありません。お尻を丸ごと使う。テキスト フィールドで正規表現を使用します。数値を適切な数値型に TryCast し、機能しない場合は検証エラーを報告します。' --. ユーザーからのすべての入力は敵対的であると想定します。

于 2008-09-26T12:51:02.310 に答える
6

とにかく、あなたが知っているように、それは悪い考えです。

次のように文字列の引用符をエスケープするようなものはどうですか: \'

置換すると次のようになります: \''

バックスラッシュが最初の引用符をエスケープする場合、2 番目の引用符は文字列を終了しています。

于 2008-09-26T12:45:33.307 に答える
6

簡単な答え: 場合によっては機能しますが、常に機能するとは限りません。あなたは自分が行うすべてのことに対してホワイトリスト検証を使用したいと考えていますが、それが常に可能であるとは限らないことを認識しているため、最善の推測のブラックリストを使用する必要があります. 同様に、パラメーター化されたストアド プロシージャをすべてで使用する必要がありますが、これも常に可能であるとは限らないため、sp_execute をパラメーターと共に使用する必要があります。

考えつくことができる使用可能なブラックリスト (およびいくつかのホワイトリストも) を回避する方法があります。

まともな記事はこちら: http://www.owasp.org/index.php/Top_10_2007-A2

実際に設置する時間を確保するための簡単な修正としてこれを行う必要がある場合は、それを行ってください。しかし、安全だとは思わないでください。

于 2008-09-26T19:32:42.750 に答える
6

例外なく、SQL インジェクションから安全にする方法は 2 つあります。プリペアド ステートメントまたはパラメータ化されたストアド プロシージャ。

于 2008-09-29T18:50:27.977 に答える
4

パトリック、すべての入力、さらには数値入力を一重引用符で囲んでいますか?数値入力があるが、その周りに一重引用符を付けていない場合は、露出があります。

于 2008-11-14T20:01:32.143 に答える
4

パラメータ化されたクエリが利用できる場合は、常にそれらを使用する必要があります。1 つのクエリがネットをすり抜けるだけで、DB が危険にさらされます。

于 2008-09-26T12:49:21.770 に答える
3

ええ、誰かがSET QUOTED_IDENTIFIER OFFを実行して二重引用符を使用するまで、それはうまくいくはずです。

編集: 悪意のあるユーザーが引用符で囲まれた識別子をオフにできないようにするのは簡単ではありません。

SQL Server Native Client ODBC ドライバーと SQL Server Native Client OLE DB Provider for SQL Server は、接続時に QUOTED_IDENTIFIER を自動的に ON に設定します。これは、ODBC データ ソース、ODBC 接続属性、または OLE DB 接続プロパティで構成できます。DB-Library アプリケーションからの接続の場合、SET QUOTED_IDENTIFIER のデフォルトは OFF です。

ストアド プロシージャが作成されると、SET QUOTED_IDENTIFIER および SET ANSI_NULLS 設定がキャプチャされ、そのストアド プロシージャの以降の呼び出しに使用されます

SET QUOTED_IDENTIFIERは、ALTER DATABASE の QUOTED_IDENTIFER 設定にも対応します。

SET QUOTED_IDENTIFIER は解析時に設定されます。解析時に設定するということは、SET ステートメントがバッチまたはストアド プロシージャに存在する場合、コード実行が実際にそのポイントに到達するかどうかに関係なく、有効になることを意味します。SET ステートメントは、ステートメントが実行される前に有効になります。

知らないうちに QUOTED_IDENTIFIER が無効になる方法はたくさんあります。確かに、これはあなたが探している決定的なエクスプロイトではありませんが、かなり大きな攻撃面です。もちろん、二重引用符もエスケープした場合は、元の場所に戻ります。;)

于 2008-10-20T21:22:33.273 に答える
3

次の場合、防御は失敗します。

  • クエリは文字列ではなく数値を期待しています
  • 次のような、単一引用符を表す他の方法がありました。
    • \039 などのエスケープ シーケンス
    • ユニコード文字

(後者の場合、置換を行った後にのみ展開されたものでなければなりません)

于 2008-10-22T13:53:16.970 に答える
1

ユーザー入力のサニタイズは、なんて醜いコードでしょう! 次に、SQL ステートメント用のぎこちない StringBuilder です。プリペアド ステートメント メソッドにより、コードがよりクリーンになり、SQL インジェクションのメリットが追加されたことは非常に素晴らしいことです。

また、なぜ車輪を再発明するのですか?

于 2008-09-26T12:45:48.517 に答える
1

一重引用符を 2 つの一重引用符 (のように見える) に変更するのではなく、アポストロフィや引用符に変更したり、完全に削除したりしないでください。

いずれにせよ、それは少し面倒です...特に、一重引用符を使用する可能性のあるもの(名前など)が合法的にある場合は...

注: この方法では、アプリで作業しているすべての人が、入力がデータベースにヒットする前にサニタイズすることを常に覚えていることも前提としていますが、これはほとんどの場合現実的ではありません。

于 2008-09-26T12:48:17.793 に答える
-1

文字列に対して機能する解決策が見つかるかもしれませんが、数値述語については、数値のみを渡すことも確認する必要があります (簡単なチェックは、int/double/decimal として解析できるかどうかです)。

それは多くの余分な作業です。

于 2008-09-26T12:45:29.813 に答える
-2

うまくいくかもしれませんが、私には少しばかげているようです。代わりに、正規表現に対してテストして、各文字列が有効であることを確認することをお勧めします。

于 2008-09-26T12:45:14.960 に答える