web-dev-qa-db-ja.com

単一引用符をエスケープし、ユーザー入力を単一引用符で囲むことにより、SQLインジェクションから保護できますか?

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

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

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

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

これに対してSQLインジェクション攻撃を仕掛ける方法はありませんが、もしこれが私のように防弾であるなら、他の誰かがすでにそれを考えていて、それが一般的な慣行であることに気付きます。

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


更新:

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

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

入力をサニタイズするためのより良い方法があることは理解していますが、上で提供した方法が機能しない理由を学ぶことに本当に興味があります。このサニタイズ方法に対してSQLインジェクション攻撃を仕掛ける具体的な方法を知っている人がいれば、ぜひ見たいと思います。

129
Patrick

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

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

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

適切な方法:

  • ホワイトリストの検証:タイプ、長さ、形式、または許容値
  • ブラックリストに登録する場合は、すぐに行ってください。引用符のエスケープは適切ですが、他の緩和策のコンテキスト内です。
  • コマンドおよびパラメーターオブジェクトを使用して、事前解析と検証を行う
  • パラメーター化されたクエリのみを呼び出します。
  • さらに良いのは、ストアドプロシージャのみを使用することです。
  • 動的SQLの使用は避け、文字列の連結を使用してクエリを作成しないでください。
  • SPを使用している場合、データベース内の権限を、必要なSPのみを実行するように制限し、テーブルに直接アクセスすることもできません。
  • また、コードベース全体がSPを介してのみDBにアクセスすることを簡単に確認できます...
84
AviD

さて、この応答は質問の更新に関連します:

「このサニタイズ方法に対してSQLインジェクション攻撃を仕掛ける特定の方法を知っている人がいれば、それを見たいと思います。」

現在、MySQLのバックスラッシュのエスケープに加えて、実際にMSSQLについて話していることを考慮すると、SQLがコードをインジェクトする3つの可能な方法が実際にあります。

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

これらは常に有効とは限らず、実際のコードに大きく依存していることを考慮してください。

  1. 2次SQLインジェクション-データベースから取得したデータに基づいてSQLクエリを再構築する場合エスケープ後、データはエスケープせずに連結され、間接的にSQLインジェクションされる場合があります。見る
  2. 文字列の切り捨て-(もう少し複雑)-シナリオでは、ユーザー名とパスワードの2つのフィールドがあり、SQLが両方を連結します。そして、両方のフィールド(または最初のフィールド)の長さには厳しい制限があります。たとえば、ユーザー名は20文字に制限されています。このコードがあるとしましょう:
username = left(Replace(sInput, "'", "''"), 20)

次に、ユーザー名が取得され、エスケープされ、20文字にトリミングされます。ここでの問題-私は20文字(たとえば、19の文字の後)に引用を貼り付け、エスケープする引用は(21文字目に)トリミングされます。次に、SQL

sSQL = "select * from USERS where username = '" + username + "'  and password = '" + password + "'"

前述の不正な形式のユーザー名と組み合わせると、パスワードはすでにoutside引用符になり、ペイロードが直接含まれます。
3。 Unicodeスマグリング-特定の状況では、looksのように見える高レベルのユニコード文字を渡すことができますが、is n't-データベースに到達するまで、突然isになります。それを検証するとき、それは引用ではないので、それは簡単に進みます...詳細については、以前の応答を参照して、元の研究へのリンク。

39
AviD

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

27
Nick Johnson

質問が出されてからかなり時間が経ったが、..

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

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

これはおそらく、「ユーザー管理」ページのシナリオで主に役立ち、「更新」ステートメントを悪用して、想定されたすべてのチェックを行わない可能性があります。

したがって、すべての引数を引用することにした場合は、文字列のサイズがどうなるかを確認し、切り捨てに陥らないようにしてください。

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

17
Jørn Jensen

入力衛生はあなたが半ばにしたいものではありません。お尻全体を使用します。テキストフィールドで正規表現を使用します。数値を適切な数値タイプに試行キャストし、機能しない場合は検証エラーを報告します。入力中の攻撃パターン(「-」など)を検索するのは非常に簡単です。ユーザーからのすべての入力が敵対的であると想定します。

8
tom.dietrich

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

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

問題は、どの文字列にユーザー入力が含まれているか(常に悪意のある可能性がある)、およびどの文字列が有効なSQLクエリであるかを確実に確認する必要があることです。トラップの1つは、データベースの値を使用する場合です。これらの値は元々ユーザー指定のものでしたか?その場合、それらもエスケープする必要があります。私の答えは、SQLクエリを構築する際に、できるだけ遅く(ただし後ほど!)サニタイズすることです。

ただし、ほとんどの場合、パラメーターバインドが最も効果的です。

8
Pontus Gagge

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

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

置換の結果:\ ''

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

6
WW.

SQLインジェクションから安全にするために、例外を使用せずに2つの方法があります。準備されたステートメントまたはパラメータ化されたストアドプロシージャ。

5
olle

簡単な答え:時々機能しますが、常に機能するわけではありません。あなたはeverythingでホワイトリスト検証を使用したいが、それは常に可能ではないことを理解しているので、あなたは最善の推測をせざるを得ないブラックリスト。同様に、パラメーター化されたストアドプロシージャをeverythingで使用する必要がありますが、これも常に可能であるとは限らないため、パラメーターを指定してsp_executeを使用する必要があります。

使用可能な任意のブラックリスト(およびホワイトリストもあります)を回避する方法があります。

適切な記事はこちら: http://www.owasp.org/index.php/Top_10_2007-A2

これを簡単な修正として必要な場合は、実際の問題を解決する時間を与えてください。しかし、あなたが安全だとは思わないでください。

5

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

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

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

4
AJ.

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

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

SQL ServerネイティブクライアントODBCドライバーおよびSQL ServerネイティブクライアントOLE SQL Server用DBプロバイダーは、接続時に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は、知らないうちにオフになる方法がたくさんあります。確かに、これはあなたが探している喫煙銃の悪用ではありませんが、かなり大きな攻撃対象領域です。もちろん、二重引用符もエスケープした場合は、始めたところに戻ります。 ;)

4
Mark Brackett

パトリック、数値入力であっても、すべての入力を単一引用符で囲んでいますか?数値を入力しているが、その引用符を一重引用符で囲まない場合、危険にさらされます。

4
Rob Kraft

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

4
Kev

ユーザー入力のサニタイズをすべてコード化するなんてWhatいコードでしょう!次に、SQLステートメントの不格好なStringBuilder。プリペアドステートメントメソッドを使用すると、コードがはるかにクリーンになり、SQLインジェクションの利点が追加されます。

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

1
JeeBee

単一引用符を(どのように見えるように)2つの単一引用符に変更するのではなく、単にアポストロフィ、引用符に変更するか、完全に削除しないのですか?

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

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

1
Kevin Fairchild

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

0
Rob

文字列に有効な解決策を見つけるかもしれませんが、数値述語の場合は、数値のみを渡すようにする必要もあります(単純なチェックはint/double/decimalとして解析できますか?)。

それは多くの余分な仕事です。

0
Joseph Daigle