質問

テーブルから行を選択する場合、基本的に次の2つのオプションがあります

int key = some_number_derived_from_a_dropdown_or_whatever
SqlCommand cmd = new SqlCommand("select * from table where primary_key = " + key.ToString());

またはパラメータを使用

SqlCommand cmd = new SqlCommand("select * from table where primary_key = @pk");
SqlParameter param  = new SqlParameter();
param.ParameterName = "@pk";
param.Value         = some_number_derived_from_a_dropdown_or_whatever;
cmd.Parameters.Add(param);

これで、SQLインジェクション攻撃の可能性があるため、最初の方法が嫌われていることはわかっていますが、この場合、パラメーターは整数であるため、実際に悪意のあるコードをインジェクトすることはできません。

これは次のとおりです:挿入されたパラメーター(上記のように、またはパラメーターがコードで作成されている場合)の使いやすさと制御のため、使用が安全であると考えるため、実動コードでオプション1を使用しますか?または、何があっても常にパラメーターを使用しますか?パラメータは100%注入しても安全ですか?

役に立ちましたか?

解決

SQLインジェクション引数をスキップします。これはあまりにもよく知られており、パラメーターと非パラメーターのSQLの側面に焦点を当てています。

SQLバッチをサーバーに送信するときは、バッチを理解するために解析する必要があります。他のコンパイラーと同様に、SQLコンパイラーはテキストから AST を生成し、操作する必要があります構文ツリー。最終的にオプティマイザーは構文ツリーを実行ツリーに変換し、最終的に実行プランを作成し、実際に実行されます。 1995年頃の暗黒時代に戻って、バッチがアドホッククエリであるかストアドプロシージャであるかに違いがありましたが、今日ではまったくありません。すべて同じです。

現在、パラメーターが違いを生じるのは、 select * from table from primary_key = @pk のようなクエリを送信するクライアントが毎回まったく同じSQLテキストを送信することです、どの値が興味を持っているかに関係なく。その場合に起こることは、上で説明した全体プロセスが短絡していることです。 SQLは(入力のハッシュダイジェストに基づいて)受信した生の未解析のテキストの実行プランをメモリ内で検索し、見つかった場合はそのプランを実行します。つまり、解析も最適化もなし、バッチはまっすぐに実行されます。毎秒数百および数千の小さなリクエストを実行するOLTPシステムでは、この高速パスによりパフォーマンスが大幅に異なります。

クエリを select * from table from primary_key = 1 の形式で送信する場合、テキストは新しいテキストである可能性が高いため、少なくともテキストを解析する必要があります。 1つは、以前のバッチとは異なります( 1 2 のような単一の文字でも、バッチ全体が異なります)。次に、結果の構文ツリーを操作し、単純なパラメーター化。クエリを自動パラメータ化できる場合、SQLは以前に他のpk値で実行された他のクエリからキャッシュされた実行プランを見つけ、そのプランを再利用するため、少なくともクエリを最適化する必要はなく、スキップします実際の実行計画を生成するステップ。しかし、完全な短絡、つまり、真のクライアントパラメーター化されたクエリで実現可能な最短経路を実現したわけではありません。

SQL Server、SQL Statisticsオブジェクトを調べることができます。サーバーのパフォーマンスカウンター。カウンター Auto-Param Attempts / sec は、SQLがパラメーターなしで受信したクエリを自動パラメーター化されたクエリに変換する必要がある1秒あたりの回数を表示します。クライアントでクエリを適切にパラメータ化すると、各試行を回避できます。また、 Failed Auto-Params / sec の数が多い場合は、クエリが最適化と実行計画の生成の全サイクルを実行していることを意味します。

他のヒント

常にオプション2)を使用します。

最初のもの は、安全ではない安全ではありませんrel = "nofollow noreferrer"> SQLインジェクション攻撃。

2番目の ははるかに安全であるだけでなく、クエリオプティマイザがクエリの見た目のために適切なクエリプランを作成する可能性が高いため、パフォーマンスも向上します常に同じ、もちろんパラメーターを期待します。

オプション1を避けるべき理由

select 要素からこの値を取得しているように見えても、誰かがHTTPリクエストを偽造し、特定のフィールドに必要なものを投稿する可能性があります。オプション1のコードは、少なくともいくつかの危険な文字/組み合わせ(つまり、単一引用符、角括弧など)を置き換える必要があります。

オプション2の使用を推奨する理由

SQLクエリエンジンは、実行プランをキャッシュし、スローされたさまざまなクエリの統計を管理できます。そのため、統一されたクエリを使用すると(もちろん、個別のストアドプロシージャを作成するのが最善です)、長期的には実行速度が向上します。

SQLインジェクションのためにパラメーターをハイジャックできる例はまだ聞いていません。そうでないことが証明されるまで、それらを安全だと思います。

動的SQLは決して安全と見なすべきではありません。 「信頼済み」であっても入力、それを取得するのは悪い習慣です。これには、ストアドプロシージャの動的SQLが含まれることに注意してください。 SQLインジェクションもそこで発生する可能性があります。

値が整数であることを制御できるため、これらはほぼ同等です。私は通常、最初の形式を使用せず、通常は2番目の形式も許可しません。これは、通常、テーブルアクセスを許可せず、ストアドプロシージャの使用を必要とするためです。また、パラメータコレクションを使用せずにSPを実行できますが、SPの AND パラメータを使用することをお勧めします:

-- Not vulnerable to injection as long as you trust int and int.ToString()
int key = some_number_derived_from_a_dropdown_or_whatever ;
SqlCommand cmd = new SqlCommand("EXEC sp_to_retrieve_row " + key.ToString()); 

-- Vulnerable to injection all of a sudden
string key = some_number_derived_from_a_dropdown_or_whatever ;
SqlCommand cmd = new SqlCommand("EXEC sp_to_retrieve_row " + key.ToString()); 

オプション1は安全なあなたの場合ですが、整数以外の変数でそのテクニックを見て使用するとどうなりますか-現在、 インジェクションに対してオープンになる可能性があります。

データベースを予防的に強化して、アプリケーションコードに欠陥がある場合でもインジェクションが有効にならないようにすることができます。アプリケーションが使用するアカウント/ロールの場合:

  • 絶対に必要な場合にのみテーブルへのアクセスを許可する
  • 絶対に必要な場合にのみビューへのアクセスを許可する
  • DDLステートメントを許可しない
  • ロールベースでのSPへの実行を許可
  • SPの動的SQLは、絶対に必要なものとしてレビューする必要があります

個人的に、私は習慣の問題として常にオプション2を使用します。言われていること:

ドロップダウン値からintへの変換を強制するため、SQLインジェクション攻撃に対する保護を提供します。誰かが投稿された情報に追加情報を挿入しようとすると、C#は悪意のあるコードをその試みを阻止する整数値に変換しようとすると例外をスローします。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top