Dot-Net

在 .NET 的動態 SQL 中清理表/列名?(防止SQL注入攻擊)

  • July 20, 2015

我正在生成一些動態 SQL,並希望確保我的程式碼不受SQL 注入的影響。

為了論證起見,這裡是一個如何生成它的最小範例:

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

在上面,tableNamecolumnName,以及任何必然@value來自不受信任的來源。由於使用佔位符可以避免@valueSQL 注入攻擊,因此可以忽略。(該命令通過 SqlCommand 執行。)

但是,tableName並且columnName 不能被綁定為佔位符,因此容易受到注入攻擊。由於這是一個“真正動態”的場景,因此沒有白名單tableNamecolumnName可用。

因此問題是:

是否有標準的內置方法來檢查和/或消毒tableNamecolumnName?(SqlConnection 或輔助類等)如果不是,那麼在使用 3rd 方庫的情況下執行此任務的好方法是什麼?

筆記:

  • 所有 SQL 標識符,包括模式,都應該被接受: eg[schema].[My Table].columntable1.
  • 可以清理標識符或檢測無效標識符。(不需要確保表/列在上下文中實際有效;生成的 SQL 可以是無效的,但必須是“安全的”。)

更新:

剛發現這個,覺得有點意思:.NET4 (EF4?) 中有一個SqlFunctions.QuoteName函式。好吧,這對我沒有幫助……

由於您使用的是 SqlConnection,因此假設這是一個 SQL Server 數據庫。

鑑於此假設,您可以使用遵循MSDN中定義的 SQL Server 標識符規則的正則表達式來驗證表和欄位名稱。雖然我是正則表達式的完全新手,但我確實發現了這個應該接近的:

[\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 獲取該表的有效列列表,然後驗證每個請求列是否在我的程式碼中的列表中。這樣,您就可以準確判斷哪些列有誤,並將該回饋提供給使用者。

我不確定你是否還在研究這個,但是這個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 ——可能來自應用程序配置中的數據提供者名稱,或者在某處硬編碼。)

引用自:https://stackoverflow.com/questions/9651582