web-dev-qa-db-ja.com

パラメーターが文字列ではない場合、SQLクエリをパラメーター化しないのは安全ですか?

SQLインジェクション に関しては、stringパラメーターをパラメーター化する必要性を完全に理解しています。それは本の中で最も古いトリックの一つです。しかし、いつそれをnotに正当化できるかSqlCommand?パラメーター化しない「安全」と見なされるデータ型はありますか?

たとえば:私は自分自身をSQLの専門家nearとは考えていませんが、潜在的に脆弱になるケースは考えられませんboolまたはintを受け入れ、クエリに直接連結するSQLインジェクション。

私の仮定は正しいですか、それとも私のプログラムに巨大なセキュリティ脆弱性を残す可能性がありますか?

説明のため、この質問にはタグが付けられています c# これは強く型付けされた言語です。 「パラメーター」と言うと、public int Query(int id)のようになります。

113
johnnyRose

私はそれが安全だと思います... technically、しかしそれは入るのがひどい習慣です。本当にこのようなクエリを書きたいですか?

var sqlCommand = new SqlCommand("SELECT * FROM People WHERE IsAlive = " + isAlive + 
" AND FirstName = @firstName");

sqlCommand.Parameters.AddWithValue("firstName", "Rob");

また、型が整数から文字列に変更される状況で脆弱になります(名前にかかわらず、文字を含む従業員番号を考えてください)。

そのため、EmployeeNumberのタイプをintからstringに変更しましたが、SQLクエリの更新を忘れていました。おっとっと。

101
Rob

制御するコンピューター(Webサーバーなど)で厳密に型指定されたプラットフォームを使用する場合、boolDateTime、またはint(およびその他の数値)値。懸念されるのは、SQLサーバーにすべてのクエリを強制的に再コンパイルさせ、どのクエリがどの頻度で実行されているか(キャッシュ管理に支障をきたす)について適切な統計を取得できないことによるパフォーマンスの問題です。

ただし、「制御するコンピューター上」の部分は重要です。そうしないと、ユーザーは、これらの値から文字列を生成するためにシステムが使用する動作を変更して、任意のテキストを含めることができるからです。

私も長期的に考えるのが好きです。今日の古くてバスト化された厳密に型指定されたコードベースが、新しいホットな動的言語への自動翻訳を介して移植され、突然型チェックが失われたが、動的コードにまだ適切な単体テストがない場合に起こること?

実際、これらの値にクエリパラメーターを使用しない正当な理由はありません。これについては正しい方法です。本当に定数である場合は、SQL文字列に値をハードコーディングしますが、そうでない場合は、単にパラメーターを使用しないのはなぜですか?それは難しいようではありません。

最終的には、これをbug自体とは呼びませんが、smellと呼びます。それ自体はバグには及ばないが、バグが近くにあるか、最終的にはそうなるという強い兆候。優れたコードは臭いを残すことを避け、優れた静的解析ツールはこれにフラグを立てます。

残念ながら、これはあなたがすぐに勝てるような種類の議論ではないことを付け加えます。 「正しい」だけでは不十分であり、同僚のつま先を踏んで自分でこの問題を修正しても、チームのダイナミクスが向上する可能性は低いようです。最終的には、助けになる以上に傷つく可能性があります。この場合のより良いアプローチは、静的分析ツールの使用を促進することです。それにより、既存のコードを目指して戻って修正する努力に正当性と信頼性が与えられます。

65
Joel Coehoorn

場合によっては、IS文字列値以外のパラメータ化されていない(連結された)変数を使用してSQLインジェクション攻撃を実行できます。Jonのこの記事を参照してください。 http:// codeblog。 jonskeet.uk/2014/08/08/the-bobbytables-culture/

ToStringが呼び出されると、一部のカスタムカルチャプロバイダーは、非文字列パラメーターをそのSQLをクエリに注入する文字列表現に変換できます。

53
Maciek

これは、非文字列型でもnot安全です。 Alwaysはパラメーターを使用します。期間。

次のコード例を検討してください。

_var utcNow = DateTime.UtcNow;
var sqlCommand = new SqlCommand("SELECT * FROM People WHERE created_on <= '" + utcNow + "'");
_

一見コードは安全に見えますが、Windowsの地域設定でいくつかの変更を行い、短い日付形式でインジェクションを追加すると、すべてが変わります。

Datetime Injection

結果のコマンドテキストは次のようになります。

_SELECT * FROM People WHERE created_on <= '26.09.2015' OR '1'<>' 21:21:43'
_

ユーザーがSQLインジェクションに簡単に変更できるカスタム負符号を定義できるので、intタイプについても同じことができます。

現在のカルチャの代わりに不変のカルチャを使用する必要があると主張することもできますが、このような文字列の連結は何度も見たことがあり、_+_を使用してオブジェクトと文字列を連結する場合は見逃しがちです。

51
Kaspars Ozols

"SELECT * FROM Table1 WHERE Id =" + intVariable.ToString()


セキュリティ
大丈夫です。
攻撃者は、型指定されたint変数に何も挿入できません。

パフォーマンス
よくない。

パラメータを使用することをお勧めします。クエリは一度コンパイルされ、次の使用のためにキャッシュされます。次回、異なるパラメーター値でも、クエリはキャッシュされ、データベースサーバーでコンパイルする必要はありません。

コーディングスタイル
悪い練習。

  • パラメータが読みやすい
  • パラメータを使用せずにクエリに慣れてから、一度間違えてこの方法で文字列値を使用した場合は、おそらくデータに別れを告げる必要があります。悪癖!


"SELECT * FROM Product WHERE Id =" + TextBox1.Text


それはあなたの質問ではありませんが、将来の読者に役立つかもしれません:

セキュリティ
災害!
Idフィールドが整数であっても、クエリはSQLインジェクションの対象となる場合があります。アプリケーションにクエリがあるとします"SELECT * FROM Table1 WHERE Id=" + TextBox1.Text、攻撃者はテキストボックスに挿入できます1; DELETE Table1とクエリは次のようになります。

"SELECT * FROM Table1 WHERE Id=1; DELETE Table1"

ここでパラメーター化されたクエリを使用したくない場合は、型付きの値を使用する必要があります。

string.Format("SELECT * FROM Table1 WHERE Id={0}", int.Parse(TextBox1.Text))


あなたの質問


同僚が整数値を連結する一連のクエリを書いたので、私の質問が生じました。すべてを調べて修正するのは私の時間の浪費かと思っていました。

これらのコードを変更しても時間の無駄ではないと思います。確かに変更が推奨されます!

同僚がint変数を使用している場合、セキュリティ上のリスクはありませんが、これらのコードを変更しても時間の無駄ではないと思います。コードを読みやすく、保守しやすくし、実行を高速化します。

23
Reza Aghaei

実際には1つの質問が2つあります。また、タイトルからの質問は、その後のコメントでOPが表明した懸念とはほとんど関係ありません。

重要なのはOPの特定のケースであることを認識していますが、Googleからの読者にとっては、「一般的な質問に答えることが重要です。私が連結しているすべてのリテラルは安全であると?」それで、私はこの後者に集中したいと思います。そして答えは

間違いなく.

説明は、ほとんどの読者が望むほど直接的ではありませんが、最善を尽くします。

私はしばらくの間、この問題について熟考してきました。その結果、 記事 (PHP環境に基づいています)で、すべてをまとめようとしました。 SQLインジェクションからの保護の問題は、文字列のエスケープ、型キャストなどの関連する狭いトピックにしばしば当てはまると思います。いくつかの対策は、単独で行うと安全と見なすことができますが、システムもシンプルなものもありません従うべきルール:これは非常に滑りやすい地面になり、開発者の注意と経験にあまりにも多くをかけます。

SQLインジェクションの問題は、特定の構文の問題に単純化することはできません。平均的な開発者が考えていたよりも広いです。 methodologicalの質問でもあります。 「特定の書式設定を適用する必要がある」だけでなく、「How行う必要がある」ことも同様です。

(この観点から、他の回答で引用されたJon Skeetの記事は、特定の構文の問題に集中し、全体的な問題に対処していないため、Edgeのケースに再び注意を向けているため、良いというよりもむしろ悪いです

全体ではなく、さまざまな構文の問題として保護の問題に対処しようとすると、多くの問題に直面します。

  • 可能なフォーマットの選択肢のリストは本当に膨大です。いくつかは簡単に見落とすことができることを意味します。またはそれらを混同します(たとえば、stringを使用してidentifierをエスケープします)。
  • 連結とは、すべての保護対策をプログラムではなくプログラマーが行う必要があることを意味します。この問題だけでも、いくつかの結果につながります。
    • このようなフォーマットは手動です。手動はextremely error proneを意味します。単に適用することを忘れることができます。
    • さらに、フォーマット手順をいくつかの集中化された機能に移動し、さらに物事をいじり、データベースに送られないデータを台無しにする誘惑があります。
  • 複数の開発者が関与した場合、問題は10倍になります。
  • 連結が使用される場合、潜在的に危険なクエリを一目で伝えることはできません:それらはall潜在的に危険です!

その混乱とは異なり、準備された声明は確かに聖杯です:

  • 従うのが簡単な1つの単純なルールの形式で表現できます。
  • これは本質的に分離できない手段であり、開発者が干渉することはできず、喜んでまたは喜んでプロセスを台無しにすることはできません。
  • インジェクションからの保護は、実際には準備されたステートメントの副作用のみであり、実際の目的は構文的に正しいステートメントを生成することです。そして、構文的に正しいステートメントは、100%の注入証明です。しかし、インジェクションの可能性があるにもかかわらず、構文が正しいことが必要です。
  • ずっと使用すると、開発者の経験に関係なくアプリケーションを保護します。たとえば、 second order injection と呼ばれるものがあります。そして、「保護するために、 すべてのユーザー入力をエスケープする 」と読む非常に強い妄想。開発者が決定する自由を取り、保護する必要があるものとそうでないものを決定する場合、一緒になって、それらは注入につながります。

(さらに考えると、プレースホルダーの現在のセットは、実際のニーズに十分ではなく、配列のような複雑なデータ構造、さらには時々追加される必要のあるSQLキーワードまたは識別子の両方のために拡張する必要があることを発見しましたクエリも動的に行われますが、開発者はそのような場合に備えて武装しておらず、文字列連結にフォールバックすることを余儀なくされますが、それは別の質問の問題です)。

興味深いことに、この質問の論争は、Stack Overflowの非常に論争的な性質によって引き起こされます。このサイトのアイデアは、直接回答するユーザーからの特定の質問を利用して一般的な目的の回答のデータベースを作成するという目標を達成することです。検索から取得します。考え方は悪くありませんper seですが、次のような状況では失敗します。ユーザーが非常に狭い質問を要求したとき、特に紛争で議論を得るために同僚(またはコードをリファクタリングする価値があるかどうかを判断する)。経験豊富な参加者のほとんどが回答を書き込もうとしていますが、スタックオーバーフローのミッションを念頭に置いて、OPだけでなく、できるだけ多くの読者に回答を提供してください。

18

セキュリティやタイプセーフの考慮事項だけを考えてみましょう。

パラメータ化されたクエリを使用する理由は、データベースレベルでパフォーマンスを向上させるためです。データベースの観点から見ると、パラメーター化されたクエリはSQLバッファー内の1つのクエリです(Oracleの用語を使用するには、すべてのデータベースが内部的に同様の概念を持っていると思いますが)。そのため、データベースは一定量のクエリをメモリに保持し、準備ができて実行準備ができています。これらのクエリは解析する必要がなく、より高速です。頻繁に実行されるクエリは通常バッファ内にあり、使用されるたびに解析する必要はありません。

なし

誰かがパラメータ化されたクエリを使用しません。この場合、バッファはデータベースエンジンによって解析および実行する必要があるほぼ同一のクエリのストリームによって継続的にフラッシュされ、頻繁に実行されるクエリでさえ何度も再解析されるため、パフォーマンスが全面的に低下します。日。私は生計のためにデータベースを調整しましたが、これはぶら下がりの果物の最大のソースの1つです。

いま

質問に答えるために、クエリに少数の個別の数値がある場合、おそらく問題を引き起こすことはなく、実際にパフォーマンスが無限に向上する可能性があります。ただし、潜在的に数百の値があり、クエリが頻繁に呼び出される場合は、システムのパフォーマンスに影響を与えるため、実行しないでください。

はい。SQLバッファを増やすことはできますが、最終的には常にインデックスやデータのキャッシュなど、メモリのその他のより重要な使用を犠牲にしてしまいます。道徳、パラメータ化されたクエリをかなり宗教的に使用するので、データベースを最適化し、重要なもののためにより多くのサーバーメモリを使用できます...

15
mcottle

Maciekの回答に情報を追加するには:

リフレクションによってアセンブリのメイン関数を呼び出すことにより、.NETサードパーティアプリのカルチャ情報を簡単に変更できます。

using System;
using System.Globalization;
using System.Reflection;
using System.Threading;

namespace ConsoleApplication2
{
  class Program
  {
    static void Main(string[] args)
    {
      Assembly asm = Assembly.LoadFile(@"C:\BobbysApp.exe");
      MethodInfo mi = asm.GetType("Test").GetMethod("Main");
      mi.Invoke(null, null);
      Console.ReadLine();
    }

    static Program()
    {
      InstallBobbyTablesCulture();
    }

    static void InstallBobbyTablesCulture()
    {
      CultureInfo bobby = (CultureInfo)CultureInfo.InvariantCulture.Clone();
      bobby.DateTimeFormat.ShortDatePattern = @"yyyy-MM-dd'' OR ' '=''";
      bobby.DateTimeFormat.LongTimePattern = "";
      bobby.NumberFormat.NegativeSign = "1 OR 1=1 OR 1=";
      Thread.CurrentThread.CurrentCulture = bobby;
    }
  }
}

これは、BobbysAppのMain関数がパブリックの場合にのみ機能します。 Mainがパブリックでない場合、他のパブリック関数を呼び出すことができます。

8
user1027167

私の意見では、使用しているパラメーターに文字列が含まれないことを保証できる場合、それは安全ですが、どのような場合でもそれを行いません。また、連結を実行しているため、パフォーマンスがわずかに低下します。私があなたに尋ねる質問は、なぜあなたはパラメータを使いたくないのですか?

7
Max

それは大丈夫ですが、決して安全ではありません。そして、セキュリティは常に入力に依存します。たとえば、入力オブジェクトがTextBoxの場合、攻撃者はテキストボックスが文字列を受け入れることができるので、ある種の検証/変換を行わなければならないので、攻撃者は何か難しいことができますユーザーが間違った入力をするのを防ぐことができます。しかし、それは安全ではありません。それと同じくらい。

3
japzdivino