2012-03-11 9 views
10

私はいくつかの動的SQLを生成していますし、私のコードはSQL injectionから安全であることを確認したいと思います。.NETの動的SQLでテーブル名または列名をサニタイズしますか? (防止SQLインジェクション攻撃は)

var sql = string.Format("INSERT INTO {0} ({1}) VALUES (@value)", 
    tableName, columnName); 
上記で

tableNamecolumnName、そしてどのような信頼できないソースから来@valueにバインドされます。ここでは議論の便宜上

は、それがどのように生成されるかの最小限の例です。プレースホルダが使用されているので、@valueはSQLインジェクション攻撃から安全であり無視することができます。 (コマンドは、SqlCommandオブジェクトを介して実行される。)

しかし、tableNamecolumnNameはプレースホルダとしてバインドされ、インジェクション攻撃に対して脆弱そのであることはできません。この「真の動的」シナリオので、tableNameまたは利用可能なcolumnNameのないホワイトリストが存在しません。

問題は、このようです:

内蔵ウェイチェックおよび/またはtableNamecolumnNameをサニタイズする標準は、ありますか? (SqlConnectionまたはヘルパークラスなど)そうでない場合、このタスクを実行する良い方法は何ですかサードパーティのライブラリを使用しないでですか?

注:

  • スキーマを含むすべてのSQL識別子、で受け入れなければならない。例えば[schema].[My Table].columntable1と同じように「安全」です。
  • sanitize識別子またはが無効な識別子であるを検出することができます。 (これは、表/列がコンテキストで実際に有効であることを確認する必要はありません。結果のSQLが無効になることができますが、「安全」でなければなりません。)

アップデート:

ジャストましたこの、それはやや面白いと思った:SqlFunctions.QuoteName機能が.NET4(?EF4)です。さて、それはしない実際に私を助けてください...

答えて

5

あなたはSqlConnectionを使用しているので、これはSQL Serverデータベースであると仮定しています。

仮定すると、定義されたSQL Server識別子の規則に従う正規表現を使用してテーブル名とフィールド名を検証できます。in MSDN私は正規表現で完了し、全くの初心者だが、私は近くに来る必要があります。この1見つけた:

[\p{L}{\p{Nd}}$#_][\p{L}{\p{Nd}}@$#_]* 

しかし、正規表現は、SQL Serverのキーワードに対処しないであろうと、それはそのテーブルを保証しませんし、および/実際には存在しています(問題はそれほど問題ではありませんでしたが)。

これが私のアプリケーションの場合、エンドユーザーがセミコロン(;)を含む要求を拒否して注入を実行しようとしていなかったことをまず確認します。

次に、有効な名前区切り文字( "、 '、[、])を削除し、テーブル名をピリオドで区切ってスキーマが指定されているかどうかを確認し、INFORMATION_SCHEMAに対してクエリを実行してテーブルの存在を検証します。テーブルの存在を決定するためにTABLES

例:

SELECT 1 
FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_NAME = 'tablename' 
AND TABLE_SCHEMA = 'tableschema' 

あなたはパラメータを使用して、このクエリを作成する場合は、あなたがさらに注入から身を守る必要があります

最後に、私が検証します。の存在各列名は、同様の手順を実行して、INFORMATION_SCHEMA.COLUMNSを使用して表が有効であると判断された列の有効性を判断することによってのみ実行されます。

SQL Serverからこのテーブルの有効な列の一覧を取得し、各要求列が自分のコード内の一覧に含まれているかどうかを確認することがあります。そうすれば、誤っている列を正確に把握し、そのフィードバックをユーザーに提供することができます。

+0

あなたは正しいです、それは確かにSQL Serverです。私は、最小限の*受け入れられた正規表現ルートに傾いていますが、何かが存在することが期待されていました(そして、テストされた;-))。スキーマのメタデータと照合することについての追加の考えが本当に好きです。 –

+0

パラメータ化されたクエリを使用してテーブル/スキーマをテストする場合は、コード内の各カラム名の存在をテーブルのカラム名の完全なリストと照合してチェックしてください。これは最小限の究極のものになります:)。 –

+0

さて、少し大きめの実装です:) –

20

あなたがまだこれを検討しているかどうかわかりませんが、DbCommandBuilderクラスはこの目的でQuoteIdentifierメソッドを提供しています。これの主な利点は、データベースに依存せず、RegEx混乱を伴わないことです。

.NET 4.5の時点で、あなたはちょうどあなたたDbConnectionオブジェクトを使用して、テーブル名とカラム名をサニタイズするために必要なすべてがあります。

DbConnection connection = GetMyConnection(); // Could be SqlConnection 
DbProviderFactory factory = DbProviderFactories.GetFactory(connection); 

// Sanitize the table name 
DbCommandBuilder commandBuilder = factory.CreateCommandBuilder(); 

string tableName = "This Table Name Is Long And Bad"; 
string sanitizedTableName = commandBuilder.QuoteIdentifier(tableName); 

IDbCommand command = connection.CreateCommand(); 
command.CommandText = "SELECT * FROM " + sanitizedTableName; 

// Becomes 'SELECT * FROM [This Table Name Is Long And Bad]' in MS-SQL, 
// 'SELECT * FROM "This Table Name Is Long And Bad"' in Oracle, etc. 

を(前4.5、あなたのDbProviderFactoryを取得するために他のいくつかの方法が必要になります。 - 多分あなたのアプリケーションの設定やハードコーディングされたどこかでデータプロバイダ名から)

+0

ありがとう、私はそのクラスが存在することを知らなかった。私はまだ.NET3.5を使用していますので、魔法の工場のヘルパーはありません:(私はそれが他の人にも使用されると確信していますが(おそらく、数年のうちに)。 –

+0

ああ、あなたはまだそれを行うことができます.NET 3.5では 'DbProviderFactory'オブジェクトを手に入れるためには他の方法が必要です。あるいは単に' DbCommandBuilder'を手動で作成するだけです。MS-SQLを常に使用していると確信できるならば、 'DbCommandBuilder commandBuilder = new SqlCommandBuilder();'を実行して、すべてのファクトリー・スキップをスキップすることができます。 –

+0

ああ、はい。それは理にかなっています - 私はLINQ2SQLを使用しています。ええ、いつもSQL Server :) –

1

SQL Serverの場合、それは識別子をサニタイズするために非常に簡単です:

// To make a string safe to use as an SQL identifier : 
// 1. Escape single closing bracket with double closing bracket 
// 2. Wrap in square brackets 
string.Format("[{0}]", identifier.Replace("]", "]]")); 

大括弧で囲まれエスケープされると、識別子として機能しない唯一のものは空の/ヌル文字列です。

+0

私はあなたがdownvotedされた理由は分かりません。なぜなら、これはまさに 'DbCommandBuilder.QuoteIdentifier'の機能なのです。 – Stijn

関連する問題