110

コードでパラメーター化された SQL クエリを使用することについて、ここで別の議論を行っています。議論には 2 つの側面があります。SQL インジェクションから保護するために常にパラメーターを使用する必要があると言う私と他の何人かと、それが必要だとは思わない他の人たちです。代わりに、SQL インジェクションを回避するために、すべての文字列で単一のアポストロフィを 2 つのアポストロフィに置き換えたいと考えています。データベースはすべて Sql Server 2005 または 2008 を実行しており、コード ベースは .NET Framework 2.0 で実行しています。

C# での簡単な例を挙げましょう。

これを使用してほしい:

string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe

他の人はこれをしたいのですが:

string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?

SafeDBString 関数は次のように定義されています。

string SafeDBString(string inputValue) 
{
    return "'" + inputValue.Replace("'", "''") + "'";
}

ここで、クエリのすべての文字列値で SafeDBString を使用する限り、安全です。右?

SafeDBString 関数を使用する理由は 2 つあります。第一に、これは石器時代から行われてきた方法であり、第二に、データベースで実行される exact クエリが表示されるため、SQL ステートメントのデバッグが容易になります。

それで。私の質問は、SQL インジェクション攻撃を回避するために SafeDBString 関数を使用するだけで本当に十分かどうかです。この安全対策を破るコードの例を見つけようとしていますが、その例は見つかりません。

これを打破できる人はいますか?どのようにしますか?

編集: これまでの返信を要約するには:

  • Sql Server 2005 または 2008 で SafeDBString を回避する方法はまだ見つかっていません。それはいいと思いますか?
  • いくつかの返信で、パラメーター化されたクエリを使用するとパフォーマンスが向上することが指摘されました。その理由は、クエリ プランを再利用できるからです。
  • また、パラメーター化されたクエリを使用すると、コードが読みやすくなり、保守が容易になることにも同意します。
  • さらに、さまざまなバージョンの SafeDBString、文字列から数値への変換、および文字列から日付への変換を使用するよりも、常にパラメーターを使用する方が簡単です。
  • パラメータを使用すると、自動型変換が得られます。これは、日付や 10 進数を扱う場合に特に便利です。
  • 最後に: JulianR が書いたように、セキュリティを自分でやろうとしないでください。データベース ベンダーは、セキュリティに多くの時間とお金を費やしています。私たちがより良くできる方法はなく、彼らの仕事をやろうとする理由もありません.

そのため、SafeDBString 関数の単純なセキュリティを破ることはできませんでしたが、他にも多くの良い議論を得ました。ありがとう!

4

21 に答える 21

83

正しい答えは次のとおりだと思います。

セキュリティを自分でやろうとしないでください。自分でやろうとするのではなく、やろうとしていることに利用できる、信頼できる業界標準のライブラリを使用してください。セキュリティについてどのような仮定を立てても、正しくない可能性があります。あなた自身のアプローチが安全に見えるかもしれませんが(そしてそれはせいぜい不安定に見えます)、何かを見落としているリスクがあり、セキュリティに関しては本当にそのチャンスを利用したいですか?

パラメータを使用します。

于 2009-05-26T13:31:01.507 に答える
72

そして、誰かが行って、' の代わりに " を使用します。パラメーターは、IMO、唯一の安全な方法です。

また、日付/数字に関する多くの i18n の問題を回避します。01/02/03 は何日ですか? どのくらいです 123,456? サーバー (app-server と db-server) は互いに一致していますか?

リスク要因に説得力がない場合、パフォーマンスはどうですか? パラメータを使用すると、RDBMS はクエリ プランを再利用できるため、パフォーマンスが向上します。文字列だけではこれを行うことはできません。

于 2009-05-26T12:45:21.893 に答える
27

議論は勝ち目がない。脆弱性を見つけることができた場合、同僚はSafeDBString関数を変更してそれを説明し、それが安全でないことをもう一度証明するように依頼します。

パラメータ化されたクエリが議論の余地のないプログラミングのベストプラクティスであることを考えると、より安全でパフォーマンスの高い方法を使用していない理由を立証する責任があります。

問題がすべてのレガシーコードの書き換えである場合、簡単な妥協案は、すべての新しいコードでパラメーター化されたクエリを使用し、そのコードで作業するときにそれらを使用するように古いコードをリファクタリングすることです。

私の推測では、実際の問題はプライドと頑固さであり、それについてできることはこれ以上ありません。

于 2009-05-26T13:57:50.640 に答える
19

まず、「置換」バージョンのサンプルが間違っています。テキストをアポストロフィで囲む必要があります。

string sql = "SELECT * FROM Users WHERE Name='" + SafeDBString(name) & "'";
SqlCommand getUser = new SqlCommand(sql, connection);

これがパラメータのもう 1 つの役割です。値を引用符で囲む必要があるかどうかを気にする必要はありません。もちろん、それを関数に組み込むこともできますが、関数に多くの複雑さを追加する必要があります: null としての 'NULL' と単なる文字列としての 'NULL' の違い、または数値とたまたま多くの数字を含む文字列。バグの別のソースです。

もう 1 つはパフォーマンスです。パラメータ化されたクエリ プランは、多くの場合、連結されたプランよりも適切にキャッシュされるため、クエリを実行する際にサーバーが 1 ステップ節約できる可能性があります。

さらに、一重引用符をエスケープするだけでは十分ではありません。 多くの DB 製品では、攻撃者が利用できる文字をエスケープする別の方法が許可されています。たとえば、MySQL では、単一引用符をバックスラッシュでエスケープすることもできます。したがって、次の「名前」の値は、関数だけで MySQL を爆破しますSafeDBString()。単一引用符を二重にすると、最初の引用符がバックスラッシュでエスケープされ、2 番目の引用符が「アクティブ」のままになるためです。

x\' OR 1=1;--


また、JulianR は次のような良い点 を挙げています。徹底したテストを行ったとしても、機能しているように見える微妙な方法で、セキュリティ プログラミングを簡単に間違えてしまいます。それから時が経ち、1 年後、あなたのシステムが 6 か月前にクラックされていたことに気付きました。

プラットフォーム用に提供されているセキュリティ ライブラリに常に可能な限り依存してください。これらは、生計を立てるためにセキュリティ コードを作成する人々によって作成され、管理できるものよりもはるかに優れたテストが行​​われ、脆弱性が見つかった場合はベンダーによってサービスが提供されます。

于 2009-05-26T12:45:30.567 に答える
10

だから私は言うだろう:

1) 組み込みのものを再実装しようとしているのはなぜですか? それはそこにあり、すぐに利用でき、使いやすく、すでに地球規模でデバッグされています。将来バグが見つかった場合は、何もしなくてもすぐに修正され、誰でも利用できるようになります。

2) SafeDBString の呼び出しを見逃さないことを保証するために、どのようなプロセスが用意されていますか? たった 1 か所でそれを見逃すと、さまざまな問題が発生する可能性があります。これらのことにどれだけ注目し、受け入れられた正しい答えが非常に簡単に到達できる場合に、その努力がどれだけ無駄になるかを考えてください.

3) Microsoft (DB およびアクセス ライブラリの作成者) が SafeDBString 実装で知っているすべての攻撃ベクトルをカバーしたことについて、どの程度確信がありますか ...

4) SQL の構造を読むのはどれくらい簡単ですか? この例では + 連結を使用しています。パラメーターは string.Format と非常によく似ており、より読みやすくなっています。

また、実際に何が実行されたのかを調べるには 2 つの方法があります。独自の LogCommand 関数、セキュリティ上の問題のない単純な関数をロールバックするか、SQL トレースを調べて、データベースが実際に何が起こっていると考えているかを調べます。

LogCommand 関数は単純です。

    string LogCommand(SqlCommand cmd)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(cmd.CommandText);
        foreach (SqlParameter param in cmd.Parameters)
        {
            sb.Append(param.ToString());
            sb.Append(" = \"");
            sb.Append(param.Value.ToString());
            sb.AppendLine("\"");
        }
        return sb.ToString();
    }

善悪を問わず、セキュリティの問題なしに必要な情報を提供してくれます。

于 2009-05-26T15:00:43.790 に答える
7

パラメータ化されたクエリを使用すると、SQL インジェクションに対する保護以上のものを得ることができます。また、実行計画のキャッシュの可能性も向上します。SQL Server クエリ プロファイラーを使用すると、「データベースで実行されている正確な sql」を引き続き表示できるため、SQL ステートメントのデバッグに関しても、実際には何も失うことはありません。

于 2009-05-26T12:47:49.643 に答える
5

私は SQL インジェクション攻撃を回避するために両方のアプローチを使用してきましたが、パラメーター化されたクエリを確実に好みます。連結クエリを使用したとき、変数をエスケープするためにライブラリ関数 (mysql_real_escape_string など) を使用しましたが、独自の実装ですべてをカバーしたとは確信できません (あなたもそうであるようです)。

于 2009-05-26T12:44:21.367 に答える
4

パラメータを使用しないと、ユーザー入力の型チェックを簡単に行うことはできません。

SQLCommand クラスと SQLParameter クラスを使用して DB 呼び出しを行う場合でも、実行中の SQL クエリを確認できます。SQLCommand の CommandText プロパティを見てください。

パラメータ化されたクエリが非常に使いやすいのに、SQL インジェクションを防止する独自のアプローチについて、私は常に少し疑っています。第二に、「常にそのように行われてきた」からといって、それが正しい方法であるとは限りません。

于 2009-05-26T12:58:58.870 に答える
3

This is only safe if you're guaranteed that you're going to pass in a string.

What if you're not passing in a string at some point? What if you pass just a number?

http://www.mywebsite.com/profile/?id=7;DROP DATABASE DB

Would ultimately become:

SELECT * FROM DB WHERE Id = 7;DROP DATABASE DB
于 2009-05-26T13:17:18.477 に答える
2

すべてにストアドプロシージャまたは関数を使用するので、問題は発生しません。

SQL をコードに記述しなければならない場合は、パラメーターを使用しますが、これだけが理にかなっています。反対者には、彼らよりも賢いハッカーがいること、そして彼らの裏をかこうとしているコードを破るより良い動機を持っていることを思い出してください。パラメータを使用すると、それは不可能であり、難しいことではありません。

于 2009-05-26T12:43:45.593 に答える
2

セキュリティの問題については大賛成です。
パラメータを使用するもう 1 つの理由は、効率です。

データベースは常にクエリをコンパイルしてキャッシュし、キャッシュされたクエリを再利用します (後続のリクエストでは明らかに高速です)。パラメータを使用すると、別のパラメータを使用しても、データベースは、パラメータをバインドする前に SQL 文字列に基づいて一致するため、キャッシュされたクエリを再利用します。

ただし、パラメーターをバインドしない場合、SQL 文字列は (パラメーターが異なる) 要求ごとに変更され、キャッシュ内の内容とは決して一致しません。

于 2009-05-26T13:26:53.497 に答える
1

「自分でやるのが悪い理由」のこの側面に対処している他の回答者は見当たりませんでしたが、SQL Truncation 攻撃を検討してください。

また、QUOTENAMEparams を使用するよう説得できない場合に役立つ T-SQL 関数もあります。エスケープされた qoute の問題の多く (すべて?) をキャッチします。

于 2009-05-26T14:49:12.690 に答える
1

可能であれば、常にパラメーター化されたクエリを使用してください。データベース内のフィールドへの入力として識別されていない場合、奇妙な文字を使用しない単純な入力でさえ、すでに SQL インジェクションを作成している可能性があります。

したがって、データベースに入力自体を識別する作業を任せてください。言うまでもなく、そうでなければエスケープまたは変更される奇妙な文字を実際に挿入する必要がある場合にも、かなりの手間が省けます。入力を計算する必要がないため、最終的に貴重な実行時間を節約することさえできます。

于 2014-10-23T09:27:53.903 に答える
1

2 年後、私は再発しました... パラメータが面倒だと思う人は誰でも、私の VS 拡張機能であるQueryFirstを試してみてください。実際の .sql ファイル (検証、Intellisense) で要求を編集します。パラメーターを追加するには、'@' で始まるパラメーターを SQL に直接入力します。ファイルを保存すると、QueryFirst はラッパー クラスを生成し、クエリを実行して結果にアクセスできるようにします。パラメータの DB タイプを検索し、それを .net タイプにマップします。これは、生成された Execute() メソッドへの入力として見つかります。これ以上簡単なことはありません. 正しい方法で実行することは、他の方法で実行するよりもはるかに迅速かつ簡単であり、SQL インジェクションの脆弱性を作成することは不可能になるか、少なくとも逆に困難になります。DB 内の列を削除して、すぐにアプリケーションのコンパイル エラーを確認できるなど、他にも大きな利点があります。

法的免責事項: QueryFirst を書きました

于 2016-09-28T12:54:37.197 に答える
1

SQL インジェクションの問題を調査しなければならなかった非常に短い時間から、値を「安全」にすることは、データに実際にアポストロフィが必要になる可能性がある状況への扉を閉めていることも意味することがわかります-誰かの名前はどうですか、例えばオライリー。

パラメータとストアド プロシージャが残ります。

そして、そうです、コードを実装するときは常に、これまでの方法だけでなく、現在知っている最善の方法で実装するようにしてください。

于 2009-05-26T13:09:21.440 に答える
1

同僚を説得するのに役立つ記事をいくつか紹介します。

http://www.sommarskog.se/dynamic_sql.html

http://unixwiz.net/techtips/sql-injection.html

個人的には、動的コードがデータベースにアクセスすることを決して許可しないことを好み、すべての連絡は sps (動的 SQl を使用するものではない) を介して行う必要があります。これは、私がユーザーに許可を与えたこと以外は何も実行できないことを意味し、内部ユーザー (管理目的で本番アクセス権を持つごく少数のユーザーを除く) は、私のテーブルに直接アクセスして大混乱を引き起こしたり、データを盗んだり、詐欺を犯したりすることはできません。金融アプリケーションを実行する場合、これが最も安全な方法です。

于 2009-05-26T13:20:29.140 に答える
1

壊れる可能性がありますが、その手段は正確なバージョン/パッチなどによって異なります.

すでに指摘されているのは、悪用される可能性のあるオーバーフロー/切り捨てバグです。

別の将来の手段は、他のデータベースと同様のバグを見つけることです。たとえば、特定の UTF8 シーケンスを使用して置換関数を操作できるため、MySQL/PHP スタックでエスケープの問題が発生しました。置換関数は、挿入文字を導入するようにだまされます。

結局のところ、代替のセキュリティ メカニズムは、意図された機能ではなく、期待される機能に依存しています。この機能はコードの本来の目的ではないため、発見された癖によって期待した機能が損なわれる可能性が高くなります。

レガシ コードが多数ある場合は、replace メソッドを応急処置として使用して、時間のかかる書き直しとテストを回避できます。新しいコードを書いている場合、言い訳はありません。

于 2009-05-26T14:58:07.617 に答える
0

SQL ステートメントのバッファ オーバーフローに関連する脆弱性 (どのデータベースだったか思い出せません) はほとんどありませんでした。

私が言いたいのは、SQL インジェクションは単に「引用符をエスケープする」以上のものであり、次に何が起こるか分からないということです。

于 2009-05-26T14:38:50.497 に答える
0

もう 1 つの重要な考慮事項は、エスケープされたデータとエスケープされていないデータを追跡することです。データが未加工の Unicode、& でエンコードされた、フォーマットされた HTML などである場合を適切に追跡していないように見える、Web やその他のアプリケーションが山ほどあります。どの文字列がエンコードされ、どの文字列がエンコードされていないかを追跡することが難しくなることは明らかです''

また、ある変数の型を変更してしまった場合にも問題になります。おそらく、以前は整数でしたが、現在は文字列になっています。今、あなたは問題を抱えています。

于 2009-05-29T02:45:04.760 に答える
0

パラメーター化されたクエリを使用するいくつかの理由を次に示します。

  1. セキュリティ - データベース アクセス層は、データで許可されていないアイテムを削除またはエスケープする方法を知っています。
  2. 関心の分離 - 私のコードは、データをデータベースが好む形式に変換する責任を負いません。
  3. 冗長性なし - このデータベースの書式設定/エスケープを行うすべてのプロジェクトにアセンブリまたはクラスを含める必要はありません。クラス ライブラリに組み込まれています。
于 2009-05-26T13:41:43.980 に答える