在 .NET 的動態 SQL 中清理表/列名?(防止SQL注入攻擊)
我正在生成一些動態 SQL,並希望確保我的程式碼不受SQL 注入的影響。
為了論證起見,這裡是一個如何生成它的最小範例:
var sql = string.Format("INSERT INTO {0} ({1}) VALUES (@value)", tableName, columnName);在上面,
tableName,columnName,以及任何必然@value來自不受信任的來源。由於使用佔位符可以避免@valueSQL 注入攻擊,因此可以忽略。(該命令通過 SqlCommand 執行。)但是,
tableName並且columnName不能被綁定為佔位符,因此容易受到注入攻擊。由於這是一個“真正動態”的場景,因此沒有白名單tableName或columnName可用。因此問題是:
是否有標準的內置方法來檢查和/或消毒
tableName和columnName?(SqlConnection 或輔助類等)如果不是,那麼在不使用 3rd 方庫的情況下執行此任務的好方法是什麼?筆記:
- 所有 SQL 標識符,包括模式,都應該被接受: eg
[schema].[My Table].column與table1.- 可以清理標識符或檢測無效標識符。(不需要確保表/列在上下文中實際有效;生成的 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 ——可能來自應用程序配置中的數據提供者名稱,或者在某處硬編碼。)