什麼是SQL注入攻擊以及我如何在C#中防止它?
[[academy-video-youtube({"vid": "dHqHrFH5Txo", "start_time": "0", "title": "什麼是SQL注入以及如何在C#中防範它?", "creator": "Tim Corey", "length": "34m 33s"})]]
SQL注入是一種程式碼注入技術,允許攻擊者通過使用者輸入將惡意的SQL程式碼發送到您的資料庫伺服器。 在他的视频"什麼是SQL注入以及如何在C#中防範它?"中,Tim Corey演示了SQL注入漏洞如何出現在真實程式碼中,展示了幾個成功的SQL注入攻擊範例(包括基於union的和破壞性的攻擊),並逐步解釋了您可以在C#中應用的實用SQL注入防範技術。 本文跟隨Tim的步驟說明,讓您能看到他顯示的具體問題和修復。
示範應用程式及其重要性
Tim從一個小型WPF示範應用程式開始,其連接到本地InjectableDB(人員和機密表)。 該應用程式的網頁形式搜尋框接受使用者輸入(姓氏),並構建SQL查詢以返回ID、名字和姓氏。 它"運作正常"——輸入Corey並獲得Tim Corey——但Tim強調核心要點:"僅僅因為應用程式正常運行並不意味著其安全。" 一個正常運行的網頁應用程式仍可能在使用者提供的輸入直接插入SQL語句時存在SQL注入漏洞。
不安全程式碼——字串連接和動態SQL
Tim展示了許多開發人員使用的確切不安全模式:
var sql = $"SELECT * FROM People WHERE LastName = '{searchText}'";
var results = connection.Query<Person>(sql);var sql = $"SELECT * FROM People WHERE LastName = '{searchText}'";
var results = connection.Query<Person>(sql);此原始查詢使用字串連接來建立SQL語句。 Tim警告:如果您看到程式碼將使用者輸入直接插入SQL查詢,請停止——這是SQL注入漏洞。 攻擊者可以製造惡意輸入來更改您的SQL命令結構,甚至運行附加的惡意SQL語句。
攻擊者如何利用——UNION和DROP
為了展示SQL注入攻擊如何工作,Tim在SQL Server中複製查詢,然後使用UNION ALL和SQL註釋(--)來隱藏尾隨字元製造注入。 Tim展示的範例惡意有效載荷:
基於UNION的SQL注入以讀取其他表:
UNION ALL SELECT ID, Username AS FirstName, Password AS LastName FROM Secrets;這將Secrets中的結果混合到原始SELECT結果集中,暴露使用者名和密碼等敏感資料。
破壞性注入以刪除表:
DROP TABLE DemoTable;這通過用分號終止第一個語句並新增破壞性命令來運行第二條SQL語句(DROP TABLE)。 Tim顯示表消失了——資料庫已被惡意SQL修改。
Tim的觀點:攻擊者不需要提前知道表或列名——他們可以從資料庫伺服器枚舉表或列名,或者僅僅嘗試盲目或基於時間的技術來發現行為。
修復1——參數化查詢
Tim的第一個且主要防禦是停止以使用者資料構建SQL字串。 用參數化查詢替換動態SQL:
string sql = "SELECT * FROM People WHERE LastName = @LastName";
var results = connection.Query<Person>(sql, new { LastName = searchText });string sql = "SELECT * FROM People WHERE LastName = @LastName";
var results = connection.Query<Person>(sql, new { LastName = searchText });Tim解釋說,參數化(事先準備好的語句樣式使用)表示資料庫將使用者提供的輸入嚴格視為資料——惡意SQL僅成為字串值,不能改變SQL結構。 這防止了許多常見的SQL注入攻擊,包括基於union的有效載荷和追加的; DROP TABLE命令。
他還建議將參數化與最小輸入驗證配對:消毒或封鎖不太可能出現在姓氏中的字元(例如,分號或--註釋標記),同時允許合法字元如撇號(O'Reilly)。 參數化查詢 + 輸入清理提供了實質上的SQL注入攻擊保護。
修復2——儲存過程
Tim接著展示了兩個儲存過程:一個不安全的儲存過程,其內部連接SQL後再執行,和一個安全的儲存過程,直接使用參數。
不安全的儲存過程從參數構建一個@sql字串並執行它——仍然易受注入攻擊。
- 安全儲存過程執行SELECT ... WHERE LastName = @LastName 並使用參數執行——安全。
Tim澄清說:如果您仍在其中構建動態SQL,儲存過程並不是自動的解決方案。 但當正確使用時(無動態SQL),儲存過程有助於集中化SQL語句,使查詢更易於參數化和審核。 儲存過程還能有助於簡化應用程式中的SQL注入防範。
不要信任任何資料——即使是資料庫資料
Tim提出的重要但往往被忽視的觀點:您不能盲目信任從您自己的SQL資料庫檢索到的資料。 攻擊者有時會在列中植入惡意有效載荷("滴答定時炸彈"),這些載荷稍後會被另一個過程串接到動態SQL中。 Tim堅持說:始終使用參數並在每一步驟消毒資料——無論它來自網頁表單、文件上傳或您自己的資料庫——以便惡意輸入後不能成為注入的途徑。
額外提示——最低權限和限制資料庫權限
除了程式碼修復外,Tim建議防禦性配置:限制您的應用程式帳戶的資料庫權限。 在他的演示中,連接使用通過整合安全性的管理員帳戶——這很危險。 相反,使用最低權限原則:
為應用程式建立一個僅具有所需權限的資料庫帳戶。
如果您使用儲存程式,則僅給該帳戶授予特定儲存程式的EXECUTE權限,其他的不授予。
- 不要給應用程式帳戶廣泛的管理員權限,允許DROP TABLE、列出所有表或讀取其他資料庫。
這減少了成功SQL注入攻擊的影響——即使有可能進行注入,攻擊者也無法超出該帳戶的允許範圍。
Tim還指出Entity Framework使這變得複雜:EF通常需要提升的權限(遷移、架構更改)。 如果您在生產中使用EF,需仔細計劃其權限和部署。
回顧——停止,參數化,消毒,限制
Tim在他的影片中總結了一個明確清單,以防止C#應用程式中的SQL注入:
停止使用包含使用者輸入的字串連接或動態SQL構建SQL語句。
使用參數化查詢/預備語句模式,以便使用者資料始終被當作資料處理。
在適當位置消毒輸入(阻止分號、SQL註釋、非預期字元)。
偏好安全的儲存程式(其內部無動態SQL)以集中查詢邏輯。
將最低權限應用于資料庫帳戶——限制應用程式資料庫使用者的權限。
- 審查程式碼(尤其是那些動態構建SQL的地方)並測試SQL注入漏洞。
Tim的最後警告:不當處理使用者輸入、動態SQL和過多的資料庫特權可導致嚴重的洩漏——敏感資料洩露,表被破壞或持續未檢測的資料外流。 將SQL注入防範視為核心安全要求,而非選擇性美化。

