SQL Server 使用 T-SQL,常見於 .NET、企業內網與報表系統。批次語句、動態 SQL、stored procedure 與資料庫角色使風險形狀不同,但根因仍是把不可信資料組進命令文字。

本篇會學到什麼
- 分辨 SqlCommand.Parameters 與字串插值
- 理解 stored procedure 內動態 SQL 仍可能注入
- 以明確 SqlDbType、長度與最小角色降低風險
必備名詞與心智模型
ADO.NET 參數會把輸入視為 literal 並提供型別資訊。sp_executesql 只有在動態 SQL 模板固定、值透過參數傳遞時才安全;若先把字串串好再交給它,名稱聽起來像參數化也不會補救。Stored procedure 也可能在內部 EXEC 拼接字串。
發生情境與生活化比喻
企業報表允許依部門、日期與欄位排序。開發者可能以為內網使用者可信,或認為 stored procedure 一定安全。實際上代理、批次匯入與報表參數仍會跨越信任邊界。
不安全的原始碼
var department = Request.Query["department"];
var sql = $"SELECT Id,Total FROM Orders WHERE Department='{department}'";
using var command = new SqlCommand(sql, connection);
using var reader = command.ExecuteReader();
此片段刻意省略連線、路由與部署設定,只用來辨識資料流,請勿放入任何服務。
逐步看懂資料如何變成查詢
- QueryString 進入 C# 字串插值
- SqlCommand 收到的 CommandText 已包含外部資料
- SQL Server 解析 T-SQL 批次
- stored procedure 若再拼接輸入會重複相同問題
- db_owner 等過大角色使注入後果擴大
修補後原始碼
const string sql = "SELECT Id,Total FROM Orders WHERE Department=@department";
using var command = new SqlCommand(sql, connection);
command.Parameters.Add("@department", SqlDbType.NVarChar, 80).Value = department;
using var reader = command.ExecuteReader();
修補的共同原則是先固定 SQL 結構,再把值獨立綁定;無法作為值綁定的識別字只能從程式內允許清單產生。
如何驗證修補
- 不用 AddWithValue 猜測關鍵欄位型別與長度
- 檢查 stored procedure 內 EXEC/EXECUTE 與動態 SQL
- 合法撇號與 Unicode 部門名不造成錯誤
- 應用登入不屬於 sysadmin 或 db_owner
- SQL Audit/Extended Events 對異常錯誤有適當告警
閱讀提醒:以下分析的目的,是讓你能在自己的程式、測試資料與授權環境中辨識風險。面對正式系統時,先取得明確授權並保存變更紀錄;不要用錯誤訊息或一次回應就推論資料外洩,也不要把文章中的概念改造成自動化探測。安全工作的完成條件,是能說明根因、修補位置、回歸結果與剩餘風險。
更多應用情境、常見誤判與觀察方法
把原理放回真實開發情境
SQL Server 使用具型別參數;動態需求由 sp_executesql 搭配參數,識別字則由固定映射控制。 實作時不要只盯著畫面上的輸入框,還要檢查 API 參數、伺服器端預設值、資料轉換與最後呼叫的資料庫介面。最實用的閱讀方式,是把每一段程式標成「外部來源、轉換、控制、資料庫 sink」,確認資料值沒有在途中重新變回 SQL 結構。
常見誤判
Stored Procedure 內若仍 EXEC 拼接字串,並不會因為『用了預存程序』就安全。 安全判斷不能只靠畫面、單次錯誤或某一個防護產品;應同時看原始碼、驅動程式實際執行方式、資料庫權限與回歸測試。若無法證明查詢結構固定,就應把它視為待修的設計風險。
開發與 SOC 可以觀察什麼
稽核高權限角色、異常批次形狀、轉型錯誤與應用帳號的非預期物件存取。 日誌宜記錄時間、路由、請求關聯 ID、查詢名稱、結果狀態與耗時,敏感值則遮罩或雜湊。偵測規則的用途是縮短發現時間;阻擋之後仍須回到程式碼修補,並搜尋其他共用元件與相同資料流。
審查時的三個追問
- 這個值最早由誰控制,經過哪些轉換?
- 它在資料庫呼叫中是資料值,還是欄位、排序、運算式等結構?
- 若預防失效,最小權限、監控與事件流程能限制並發現多少影響?
開發與維運防禦清單
- 所有值使用 SqlParameter 或安全 ORM 參數
- 動態識別字映射固定清單
- sp_executesql 的 statement 與 parameter definition 分離
- 限制跨資料庫權限、linked server 與高權限 service account
重點整理與下一篇
下一篇轉向 PostgreSQL,說明 $1 參數、PQexecParams、單一命令限制、明確型別轉換與 schema/search_path 的安全考量。
參考資料與更新日期
更新日期:2026-07-20














