什麼是 SQL 注入?
SQL Injection 發生於應用程式將使用者輸入直接拼接至 SQL 命令字串,導致惡意輸入改變查詢語意、竊取/竄改/刪除資料,甚至執行系統指令。典型攻擊流程:
- 登入頁面接受帳號/密碼輸入。
- 後端拼接:
"SELECT * FROM Users WHERE login='" + txtLogin.Text + "' AND password='" + txtPassword.Text + "'"
- 攻擊者輸入:
' OR '1'='1
- 實際 SQL 變為:
SELECT * FROM Users WHERE login='' OR '1'='1' AND password='' OR '1'='1',永真條件繞過驗證。
六大防護層級
1. 參數化查詢(首選、最有效)
// 錯誤示範
string sql = "SELECT * FROM Users WHERE UserName='" + txtUser.Text + "'";
// 正確做法
string sql = "SELECT * FROM Users WHERE UserName=@UserName";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@UserName", txtUser.Text);
參數化查詢將程式碼與資料分離,資料庫引擎將參數視為字面值而非可執行片段,徹底阻斷注入。
2. 預存程序
- 所有邏輯封裝於資料庫,應用程式僅呼叫
EXEC sp_Login @User, @Pwd。
- 搭配
CommandType.StoredProcedure 與參數集合。
- 授權原則:應用程式帳號僅具
EXECUTE 特定預存程序權限,無直接資料表存取權。
3. 輸入驗證與清理(纵深防禦)
| 手法 | 說明 | 適用場景 |
| 白名單驗證 | Regex 限定允許字元(如 ^[a-zA-Z0-9_]{3,20}$) | 帳號、編號、代碼等格式固定欄位 |
| 長度限制 | 前後端同步限制 maxlength、StringLength 屬性 | 所有輸入欄位 |
| 單引號轉義 | 單引號替換為兩個單引號('') | 動態 SQL 無法參數化的極少數情境 |
| 移除註釋符 | 過濾 --、/* */、; | 輔助防禦,不可單獨依賴 |
4. 最小權限原則
- Web 應用程式連線資料庫使用專用低權限帳號(非 sa、非 db_owner)。
- 僅授予必要資料表的
SELECT/INSERT/UPDATE/DELETE,嚴禁 DROP、TRUNCATE、xp_cmdshell 等高危權限。
- 讀寫分離:查詢用唯讀帳號,寫入用低權限帳號。
5. 錯誤資訊隱藏
web.config 設定 <customErrors mode="RemoteOnly" />。
- 避免在例外訊息中洩露 SQL 片段、資料表結構、連線字串。
- 記錄詳細錯誤至伺服器端日誌(Serilog、NLog、ELMAH),前端僅顯示友善提示。
6. 進階防護:WAF、ORM、程式碼分析
- Web Application Firewall:Azure WAF、ModSecurity、Imperva 等攔截已知攻擊模式。
- Entity Framework / Dapper:ORM 內建參數化,Dapper 以
@param 方式安全執行原生 SQL。
- 靜態程式碼分析:SonarQube、Roslyn Analyzers 偵測字串拼接 SQL、硬編碼密碼等反模式。
- 資料庫審計:SQL Server Audit、Extended Events 記錄可疑查詢、失敗登入、結構變更。
實戰檢核清單
[ ] 所有資料庫存取是否使用參數化查詢或預存程序?
[ ] 連線字串帳號是否為最低權限專用帳號?
[ ] 所有使用者輸入是否經過伺服器端白名單驗證?
[ ] 前端 maxlength、Regex 驗證是否與後端一致?
[ ] 錯誤頁面是否隱藏技術細節?
[ ] 是否部署 WAF 或啟用資料庫審計?
[ ] 定期進行滲透測試與程式碼審查?
總結
SQL 注入雖歷久不衰,但透過「參數化查詢為核心、最小權限為基石、多層驗證為纵深、監控審計為保障」的系統性防禦,可將風險降至最低。永遠記住:永不信任任何外部輸入,永不拼接 SQL 字串。
- 上一篇文章: ASP.NET 2.0 資料庫程式設計技術概覽:從 ADO.NET 到資料源控制項的革命
- 下一篇文章: ASP.NET 將檔案上傳至資料庫完整實作指南
Copyright ©
程式碼之家 All Rights Reserved