web-dev-qa-db-ja.com

SQL ServerでのSQLインジェクションによって、単一引用符をエスケープする衛生をどのように無効にできますか?

これを開始するために、パラメーター化されたクエリが最適なオプションであることを十分に認識していますが、以下に示す戦略が脆弱になる原因は何かを尋ねています。以下のソリューションは機能しないと人々は主張しているので、なぜ機能しないのかの例を探しています。

動的SQLが、SQL Serverに送信される前に次のエスケープを使用してコードに組み込まれている場合、どのようなインジェクションがこれを無効にすることができますか?

string userInput= "N'" + userInput.Replace("'", "''") + "'"

同様の質問に回答しました here ですが、ここで答えが当てはまるとは思いません。

SQL Serverでは、単一引用符を「\」でエスケープすることはできません。

私は、SQL Smugglingwith Unicode(outlined here )は、生成される文字列がUnicodeとしてマークされているという事実によって妨げられると信じています一重引用符の前のN私の知る限り、SQL Serverが自動的に単一引用符に変換する他の文字セットはありません。エスケープされていない一重引用符がなければ、インジェクションが可能だとは思わない。

String Truncationも実行可能なベクトルではないと思います。 nvarcharの最大サイズは2GB Microsoftによると であるため、SQL Serverは確かに切り捨てを行いません。 2 GBの文字列は、ほとんどの状況では実行不可能であり、私の状況では不可能です。

二次注入は可能ですが、次の場合に可能です:

  1. データベースに入るすべてのデータは、上記の方法を使用してサニタイズされます
  2. データベースの値が動的SQLに追加されることはありません(動的SQL文字列の静的部分のテーブル値を参照できるのに、どうしてそうするのでしょうか?)。

私は、これがパラメーター化されたクエリを使用するよりも優れている、または代替手段であることを提案していませんが、私が概説したものがどのように脆弱かを知りたいです。何か案は?

64
GBleaney

このエスケープ関数が失敗する場合がいくつかあります。最も明白なのは、単一引用符が使用されていない場合です。

string table= "\"" + table.Replace("'", "''") + "\""
string var= "`" + var.Replace("'", "''") + "`"
string index= " " + index.Replace("'", "''") + " "
string query = "select * from `"+table+"` where name=\""+var+"\" or id="+index

この場合、二重引用符、バックティックを使用して「ブレークアウト」できます。最後のケースでは、「ブレークアウト」するものは何もないので、1 union select password from users--または攻撃者が望むSQLペイロードを書くことができます。

このエスケープ関数が失敗する次の条件は、文字列がエスケープされた後にサブ文字列が取得された場合です(およびyesこのような脆弱性が見つかりました野生):

string userPassword= userPassword.Replace("'", "''")
string userName= userInput.Replace("'", "''")
userName = substr(userName,0,10)
string query = "select * from users where name='"+userName+"' and password='"+userPassword+"'";

この場合、abcdefgji'というユーザー名は、エスケープ関数によってabcdefgji''に変換され、サブストリングを取得することでabcdefgji'に戻ります。これは、任意のsqlステートメントにパスワード値を設定することで利用できます。この場合、or 1=1--はsqlとして解釈され、ユーザー名はabcdefgji'' and password=として解釈されます。結果のクエリは次のとおりです。

select * from users where name='abcdefgji'' and password=' or 1=1-- 

T-SQLおよび他の高度なSQLインジェクションテクニックは、既に言及されています。 SQL Serverアプリケーションでの高度なSQLインジェクション はすばらしい論文であり、まだ読んでいない場合は読むべきです。

最後の問題はユニコード攻撃です。このクラスの脆弱性は、エスケープ関数がマルチバイトエンコーディングを認識しないために発生し、これは 攻撃者がエスケープ文字を「消費」するために使用 である可能性があります。文字列の前に「N」を追加しても、文字列の後半のマルチバイト文字の値に影響しないため、役に立ちません。ただし、GBKユニコード文字列を受け入れるようにデータベースを構成する必要があるため、このタイプの攻撃は非常にまれです(MS-SQLでこれができるかどうかはわかりません)。

2次コードインジェクションは引き続き可能です。この攻撃パターンは、攻撃者が制御するデータソースを信頼することによって作成されます。エスケープは、制御文字を文字リテラルとして表すために使用されます。開発者がselectから取得した値をエスケープするのを忘れてから、この値を別のクエリで使用すると、bam攻撃者は文字リテラルの単一引用符を自由に使用できます。

すべてをテストし、何も信頼しない

40
rook

いくつかの追加規定により、上記のアプローチはSQLインジェクションに対して脆弱ではありません。考慮すべき攻撃の主なベクトルは、SQLスマグリングです。似たようなユニコード文字が予期しない方法で変換されると、SQLスマグリングが発生します(例: `変更する ')。アプリケーションスタックがSQLスマグリングに対して脆弱である可能性のある場所がいくつかあります。

  • プログラミング言語はユニコード文字列を適切に処理しますか?言語がユニコードを認識しない場合、ユニコード文字のバイトを単一引用符として誤認識してエスケープする可能性があります。

  • クライアントデータベースライブラリ(ODBCなど)はUnicode文字列を適切に処理しますか? .NetフレームワークのSystem.Data.SqlClientは処理しますが、Windows 95時代の古いライブラリはどうですか?サードパーティODBCライブラリは実際に存在します。ODBCドライバがクエリ文字列でUnicodeをサポートしていない場合はどうなりますか?

  • DBは入力を正しく処理しますか? SQLの最新バージョンは、N ''を使用していることを前提にしていますが、SQL 6.5はどうですか? SQL 7.0?特定の脆弱性は認識していませんが、1990年代の開発者にとってこれはレーダーではありませんでした。

  • バッファオーバーフロー?もう1つの懸念は、引用符で囲まれた文字列が元の文字列よりも長いことです。 Sql Serverのどのバージョンで入力の2GB制限が導入されましたか?その前に制限は何でしたか?古いバージョンのSQLでは、クエリが制限を超えたときに何が起こりましたか?ネットワークライブラリの観点から、クエリの長​​さに制限はありますか?または、プログラミング言語の文字列の長さについて?

  • Replace()関数で使用される比較に影響する言語設定はありますか? .Netは常にReplace()関数のバイナリ比較を行います。それは常に当てはまりますか? .NETの将来のバージョンがapp.configレベルでその動作のオーバーライドをサポートする場合はどうなりますか? Replace()の代わりに正規表現を使用して単一引用符を挿入するとどうなりますか?コンピューターのロケール設定はこの比較に影響しますか?動作の変更が発生した場合、SQLインジェクションに対して脆弱ではない可能性がありますが、DBに到達する前に一重引用符のように見えるユニコード文字を一重引用符に変更することにより、文字列を誤って編集した可能性があります。

したがって、SQL Serverの現在(2005-2012)バージョンに対して組み込みのSqlClientライブラリを持つ.Netの現在のバージョンでC#でSystem.String.Replace()関数を使用していると仮定すると、あなたのアプローチは脆弱。物事を変え始めると、約束はできません。パラメータ化されたクエリアプローチは、効率、パフォーマンス、および(場合によっては)セキュリティのための正しいアプローチです。

[〜#〜] warning [〜#〜]上記のコメントは、この手法を支持するものではありません。これがSQLを生成するための間違ったアプローチである理由は他にもいくつかあります。ただし、これらの詳細は、この質問の範囲外です。

新規開発にはこの技術を使用しないでください。

新規開発にはこの技術を使用しないでください。

新規開発にはこの技術を使用しないでください。

17
StrayCatDBA

クエリパラメータの使用 は、引用符をエスケープするよりも優れ、簡単で、高速です。


コメントしてください。パラメータ化は認められましたが、強調するに値します。パラメータ化できるのに、なぜエスケープを使用したいのですか?

SQL Serverアプリケーションでの高度なSQLインジェクション で、テキスト内の「置換」という単語を検索し、その時点から、ユーザー入力をエスケープした後でも開発者がSQLインジェクション攻撃を誤って許可したいくつかの例をお読みください。


\が一部の文字セットで有効なマルチバイト文字の半分になるため、\で引用符をエスケープすると脆弱性が生じるEdgeの場合があります。ただし、\はエスケープ文字ではないため、これはケースに適用できません。

他の人が指摘したように、文字列リテラルまたは日付リテラル以外の何かのために、SQLに動的コンテンツを追加することもできます。テーブルまたは列の識別子は、SQLでは"、Microsoft/Sybaseでは[]で区切られます。もちろん、SQLキーワードには区切り文字はありません。これらの場合、補間する値をホワイトリストすることをお勧めします。

結論として、エスケープ効果的な防御であるということです(一貫して行うことを保証できる場合)。それがリスクです。アプリケーションの開発者チームの1人がステップを省略し、文字列の補間を安全に行えない可能性があります。

もちろん、パラメータ化のような他のメソッドにも同じことが言えます。一貫して行う場合にのみ効果的です。しかし、適切なタイプのエスケープを見つけるよりも、パラメーターを使用する方が簡単です。また、開発者は便利で、速度を落とさない方法を使用する可能性が高くなります。

9
Bill Karwin

ユーザー提供の入力がコマンドとして解釈される場合、SQLインジェクションが発生します。ここで、コマンドとは、認識された データ型 リテラルとして解釈されないものを意味します。

これで、ユーザーの入力をデータリテラルのみ、具体的には文字列リテラルのみで使用している場合、ユーザー入力は、文字列リテラルコンテキストを離れることができる場合にのみ、文字列データとは異なるものとして解釈されます。文字列またはUnicode文字列リテラルの場合、リテラルデータを囲むのは単一引用符であり、埋め込まれた単一引用符は2つの単一引用符で表す必要があります。

したがって、文字列リテラルのコンテキストを終了するには、2つの単一引用符が文字列リテラルの終了区切り文字としてではなく、文字列リテラルデータとして解釈されるため、単一の単一引用符(sic)を指定する必要があります。

そのため、ユーザーが指定したデータの単一引用符を2つの単一引用符に置き換える場合、ユーザーが文字列リテラルコンテキストを離れることはできません。

4
Gumbo

SQLインジェクションは、ユニコード経由で発生する可能性があります。 Webアプリに次のようなURLがある場合:

http:// mywebapp/widgets /?Code = ABC

code = 'ABC'のウィジェットからselect *のようなSQLを生成します

しかし、ハッカーはこれを入力します:

http:// mywebapp/widgets /?Code = ABC%CA%BC; drop テーブルウィジェット-

sQLは、コードが「ABC」であるウィジェットからselect *のように表示されます。ドロップテーブルウィジェット-

sQL Serverは2つのSQLステートメントを実行します。 1つは選択を行い、もう1つはドロップを行います。コードはおそらく、URLエンコードされた%CA%BCを「修飾子文字アポストロフィ」であるユニコードU02BCに変換します。 .NetのReplace関数は、それを一重引用符として扱いません。ただし、Microsoft SQL Serverはそれを一重引用符のように扱います。これはおそらくSQLインジェクションを許可する例です。

string badValue = ((char)0x02BC).ToString();
badValue = badValue + ";delete from widgets--";
string sql = "SELECT * FROM WIDGETS WHERE ID=" + badValue.Replace("'","''");
TestTheSQL(sql);
3
Rob Kraft

文字列の連結を行っている場合、おそらく100%安全な方法はありません。できることは、各パラメーターのデータ型を確認し、すべてのパラメーターがそのような検証に合格したら、実行を続行することです。たとえば、パラメータをint型にする必要があり、intに変換できないものを取得している場合は、単に拒否します。

ただし、nvarcharパラメータを受け入れている場合は機能しません。

他の人がすでに指摘したように。最も安全な方法は、パラメーター化されたクエリを使用することです。

2
George Wesley