清洗函數 `dbo.fn_SanitizeInput`
CREATE FUNCTION dbo.fn_SanitizeInput (@input NVARCHAR(4000))
RETURNS NVARCHAR(4000)
AS
BEGIN
-- 移除常見 SQL 注入特徵字元
SET @input = REPLACE(@input, N'''', N''); -- 單引號
SET @input = REPLACE(@input, N'"', N''); -- 雙引號
SET @input = REPLACE(@input, N':', N''); -- 冒號 (ODBC 逸出)
SET @input = REPLACE(@input, N'-', N''); -- 註解起始 --
SET @input = REPLACE(@input, N';', N''); -- 批次分隔符 (防堆疊注入)
SET @input = REPLACE(@input, N'/*', N''); -- 註解開始
SET @input = REPLACE(@input, N'*/', N''); -- 註解結束
SET @input = REPLACE(@input, N'xp_', N''); -- 擴充預存程序前綴
SET @input = REPLACE(@input, N'sp_', N''); -- 系統預存程序前綴
RETURN @input;
END
GO
注意:黑名單過濾**非絕對安全**,繞過手法層出不窮 (Unicode 同形字、編碼、截斷等)。**根本防禦請見下文**。
預存程序整合範例:登入驗證
CREATE PROCEDURE dbo.usp_ValidateLogin
@username NVARCHAR(50),
@password NVARCHAR(50),
@result INT OUTPUT -- 0=成功, -1=帳號不存在, -2=密碼錯誤, -3=鎖定
AS
BEGIN
SET NOCOUNT ON;
-- 1. 輸入清洗 (防禦深度:即使參數化仍清洗,防二次注入/日誌污染)
SET @username = dbo.fn_SanitizeInput(@username);
SET @password = dbo.fn_SanitizeInput(@password);
-- 2. 參數化查詢 (核心防線:查詢計畫與資料分離)
DECLARE @stored_hash NVARCHAR(128), @status TINYINT;
SELECT @stored_hash = PasswordHash, @status = AccountStatus
FROM dbo.Users
WHERE Username = @username; -- 參數化,無注入風險
IF @@ROWCOUNT = 0
BEGIN
SET @result = -1; RETURN; -- 帳號不存在
END
IF @status = 1 -- 鎖定狀態
BEGIN
SET @result = -3; RETURN;
END
-- 3. 密碼驗證 (此處簡化為明文比對,實務必用 BCrypt/Argon2/PBKDF2 驗證雜湊)
IF @stored_hash = @password -- 實務:IF dbo.fn_VerifyHash(@password, @stored_hash) = 1
BEGIN
SET @result = 0; -- 成功
END
ELSE
BEGIN
SET @result = -2; -- 密碼錯誤
-- 可加入失敗計數、達閾值鎖定邏輯
END
END
GO
根本防禦三大支柱 (清洗函數只是輔助)
- 參數化查詢 / 預存程序參數:所有動態值**絕不**字串拼接進 SQL。`sp_executesql` + 參數、`SqlCommand.Parameters.Add`、ORM 參數化。
- 最小權限帳號:應用連線帳號**僅擁有**必要物件的 `SELECT/INSERT/UPDATE/DELETE/EXECUTE` 權限,**無** `sysadmin`、`db_owner`、`ddl_admin`、`xp_cmdshell` 權限。
- 輸入驗證 (白名單):應用層依業務規則驗證 (長度、格式、範圍、字元集),**拒絕**不符合預期的輸入,而非試圖過濾壞字元。
補充:動態 SQL 安全模板
CREATE PROCEDURE dbo.usp_SearchUsers
@searchTerm NVARCHAR(100),
@role NVARCHAR(20) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX), @params NVARCHAR(MAX);
SET @sql = N'SELECT UserID, Username, Email FROM dbo.Users WHERE 1=1';
SET @params = N'@term NVARCHAR(100), @role NVARCHAR(20)';
IF @searchTerm IS NOT NULL
SET @sql += N' AND (Username LIKE @term OR Email LIKE @term)';
IF @role IS NOT NULL
SET @sql += N' AND Role = @role';
EXEC sp_executesql @sql, @params,
@term = N'%' + @searchTerm + N'%', -- 參數化,安全
@role = @role;
END
動態 SQL **必須**使用 `sp_executesql` + 參數化,**禁止**在 `@sql` 字串中直接拼接變數值。