18

私はちょうど奇妙なことに遭遇しました...巨大なSQLステートメントを取り、いくつかのユーザー値に基づいて検索と置換を行うことによってコードでそれを変更し、それを次のようにSQLServerに渡すコードがサイトにありますクエリ。

これは、ユーザー値をパラメーターとして、ストアドプロシージャへのパラメーター化されたクエリとしてよりクリーンになると思っていましたが、よく見ると、なぜそれを実行しているのかがわかります...彼らが選択しているテーブルはそれらのユーザー値にさまざまに依存します。

たとえば、あるケースでは、値が( "FOO"、 "BAR")の場合、クエリは "SELECT *FROMFOO_BAR"のようになります。

これを行うための簡単で明確な方法はありますか?私が試していることはすべてエレガントではないようです。

編集:もちろん、ストアドプロシージャでSQLを動的に生成し、それを実行することもできます(bleh)が、その時点で、何かを得たかどうか疑問に思っています。

EDIT2:いくつかのインテリジェントな方法でテーブル名をリファクタリングします。たとえば、新しい列として異なる名前を持つ1つのテーブルにすべてを含めることは、これをすべて解決するための優れた方法であり、複数の人が直接指摘したり、ほのめかしたりします。残念ながら、この場合はオプションではありません。

4

10 に答える 10

48

まず第一に、このようなクライアントアプリでSQLコマンドの作成を絶対に行わないでくださいこれSQLインジェクションです。(独自の特権を持たない管理ツールでは問題ありませんが、共有使用アプリケーションでは問題ありません)。

第二に、はい、ストアドプロシージャへのパラメータ化された呼び出しは、よりクリーンで安全です。

ただし、これを行うには動的SQLを使用する必要があるため、実行されたクエリのテキストに渡された文字列を含めたくありません。代わりに、渡された文字列を使用して、ユーザーが途中でクエリを実行できるようにする必要がある実際のテーブルの名前を検索する必要があります。

簡単な単純な例を次に示します。

CREATE PROC spCountAnyTableRows( @PassedTableName as NVarchar(255) ) AS
-- Counts the number of rows from any non-system Table, *SAFELY*
BEGIN
    DECLARE @ActualTableName AS NVarchar(255)

    SELECT @ActualTableName = QUOTENAME( TABLE_NAME )
    FROM INFORMATION_SCHEMA.TABLES
    WHERE TABLE_NAME = @PassedTableName

    DECLARE @sql AS NVARCHAR(MAX)
    SELECT @sql = 'SELECT COUNT(*) FROM ' + @ActualTableName + ';'

    EXEC(@SQL)
END

なぜこれがより安全なのかとかなり質問する人もいます。うまくいけば、小さなボビーテーブルがこれをより明確にすることができます:0 代替テキスト


その他の質問への回答:

  1. QUOTENAMEだけでは安全が保証されません。MSは私たちにそれを使用することを勧めていますが、ハッカーにだまされないという保証はありません。参考までに、本当のセキュリティは保証がすべてです。QUOTENAMEを使用したテーブルルックアップは別の話であり、壊れることはありません。

  2. この例では、QUOTENAMEは厳密には必要ありません。通常、INFORMATION_SCHEMAでのルックアップ変換だけで十分です。QUOTENAMEは、完全で正しいソリューションを含めることがセキュリティの良い形式であるため、ここにあります。ここでのQUOTENAMEは、実際には、潜在的な注入として知られている、明確ではあるが同様の潜在的な問題から保護しています。


INFORMATION_SCHEMA.COLUMNS動的な列名とテーブル でも同じことができることに注意してください。

代わりに、パラメーター化されたSQLクエリを使用して、ストアドプロシージャの必要性を回避することもできます(https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.parameters?view=を参照)。 netframework-4.8)。しかし、ストアドプロシージャは、このような場合に、より管理しやすく、エラーが発生しにくいセキュリティ機能を提供すると思います。

于 2009-08-07T20:27:41.103 に答える
5

(残念ながら)残念ながら、これを行う方法はありません。動的SQL生成以外の方法で、保存されたコードにパラメーターとして渡されたテーブル名を使用することはできません。SQLコードを生成する場所を決定する場合、保存されているコードよりもアプリケーションコードの方が好きです。通常、アプリケーションコードはより高速で、保守が容易です。

使用しているソリューションが気に入らない場合は、より詳細な再設計をお勧めします(つまり、スキーマ/アプリケーションロジックを変更して、テーブル名をパラメーターとしてどこにでも渡す必要がなくなるようにします)。

于 2009-08-07T20:20:18.437 に答える
2

ストアドプロシージャでSQLを動的に生成することに反対します。それはあなたをトラブルに巻き込み、インジェクションの脆弱性を引き起こす可能性があります。

代わりに、クエリの影響を受ける可能性のあるすべてのテーブルを分析し、クエリに使用するテーブルを決定する何らかの列挙を作成します。

于 2009-08-07T20:14:43.750 に答える
2

ORMソリューションを使用したほうがよいようです。

ストアドプロシージャに動的SQLが表示されると、うんざりします。

于 2009-08-07T20:21:50.400 に答える
1

検討できることの1つは、有効なテーブルごとに1回ずつ、必要な同じSQLコマンドを含むcaseステートメントを作成し、テーブル名を文字列としてこのプロシージャに渡して、実行するコマンドをcaseに選択させることです。

ちなみに、セキュリティ担当者として、有効なテーブルがあることを確認するためにシステムテーブルから選択するように指示する上記の提案は、私には無駄な操作のように思えます。誰かがQUOTENAME()を渡してインジェクトでき​​る場合、インジェクションはシステムテーブルと基になるテーブルで機能します。これが有効なテーブル名であることを確認するのに役立つ唯一のことです。QUOTENAME()をまったく使用していないため、上記の提案がより良いアプローチだと思います。

于 2011-03-01T15:01:21.213 に答える
0

これらのテーブルの列のセットが同じか異なるかに応じて、長期的には2つの方法でアプローチします。

1)同じ場合は、セレクターとして使用される新しい列を作成してみませんか。その値は、ユーザーが指定したパラメーターから取得されます。(これはパフォーマンスの最適化ですか?)

2)それらが異なる場合、それらの取り扱いも異なる可能性があります。そのため、選択/処理コードを別々のブロックに分割し、それらを別々に呼び出すことは、私にとって最もモジュール化されたアプローチのようです。「select*from」の部分を繰り返しますが、このシナリオでは、テーブルのセットが有限であることが望まれます。

呼び出し元のコードがテーブル名の任意の2つの部分を指定して選択を実行できるようにすることは、非常に危険です。

于 2009-08-07T20:28:23.270 に答える
0

データが複数のテーブルに分散している理由はわかりませんが、基本の1つを破っているようです。データは、テーブル名としてではなく、テーブル内にある必要があります。

テーブルのレイアウトがほぼ同じである場合は、代わりにデータを1つのテーブルに配置するのが最適かどうかを検討してください。これにより、動的クエリの問題が解決され、データベースのレイアウトがより柔軟になります。

于 2009-08-07T20:37:52.037 に答える
0

ユーザー入力値に基づいてテーブルをクエリする代わりに、代わりにプロシージャを選択できます。つまり、
1。プロシージャFOO_BAR_prcを作成し、その中にクエリ'select * from foo_bar'を配置します。これにより、クエリがデータベースによってプリコンパイルされます。
2.次に、ユーザー入力に基づいて、アプリケーションコードから正しい手順を実行します。

約50のテーブルがあるので、これは実行可能な解決策ではないかもしれませんが、それはあなたの側で多くの作業を必要とするでしょう。

于 2009-08-07T20:38:44.063 に答える
0

実際、ストアドプロシージャでテーブルを作成するためにテーブル名を渡す方法を知りたいと思いました。いくつかの答えを読み、最後にいくつかの変更を試みることで、最終的に、パラメーターとして渡された名前を持つテーブルを作成することができました。これは、他のユーザーがエラーをチェックするためのストアドプロシージャです。

USE[データベース名]GO/ ******オブジェクト:StoredProcedure [dbo]。[sp_CreateDynamicTable]スクリプト日付:2015年6月20日16:56:25 ****** / SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATEPROCEDURE[dbo]。[sp_CreateDynamicTable]@tNamevarchar(255)AS BEGIN SET NOCOUNT ON; DECLARE @SQL nvarchar(max)

SET @SQL = N'CREATE TABLE [DBO].['+ @tName + '] (DocID nvarchar(10) null);'

    EXECUTE sp_executesql @SQL

終わり

于 2015-06-20T11:33:30.947 に答える
0

@RBarry Youngクエリ文字列の@ActualTableNameに角かっこを追加する必要はありません。これは、INFORMATION_SCHEMA.TABLESのクエリの結果にすでに含まれているためです。そうしないと、実行時にエラーが発生します。

CREATE PROC spCountAnyTableRows(@PassedTableName as NVarchar(255))AS-システム以外のテーブルからの行数をカウントします。SAFELYBEGIN DECLARE @ActualTableName AS NVarchar(255)

SELECT @ActualTableName = QUOTENAME( TABLE_NAME )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = @PassedTableName

DECLARE @sql AS NVARCHAR(MAX)
--SELECT @sql = 'SELECT COUNT(*) FROM [' + @ActualTableName + '];'

-- changed to this
SELECT @sql = 'SELECT COUNT(*) FROM ' + @ActualTableName + ';'

EXEC(@SQL)

終わり

于 2016-08-12T09:14:22.693 に答える