首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

資料庫查詢中特殊字元處理完全指南:SQL Server 資料型別對照與參數化防禦實戰

     編輯:關於SqlServer

核心問題:字串拼接導致的語法錯誤與注入風險

當使用者輸入包含單引號 (`'`)、雙引號 (`"`)、管線符 (`|`)、連字號 (`&`)、分號 (`;`) 等字元,直接拼接至 SQL 字串會導致:

  • 語法錯誤:`SELECT * FROM Users WHERE Name = 'O'Brien'` → 單引號截斷字串。
  • 邏輯竄改:`Name = 'admin' --` 註釋後續條件。
  • SQL Injection:`'; DROP TABLE Users; --` 執行任意指令。

SQL Server 主要資料型別完整對照表

分類型別範圍/長度儲存備註
精確數值BIT0/1/NULL1 bit (8欄=1 byte)布林旗標
TINYINT0~2551 byte非負小整數
SMALLINT-32,768~32,7672 bytes
INT-2,147,483,648~2,147,483,6474 bytes最常用整數
BIGINT-2^63~2^63-18 bytes大整數
精確十進位DECIMAL(p,s) / NUMERIC(p,s)p=1~38, s=0~p5~17 bytes金額、精確計算首選
MONEY / SMALLMONEY±922 兆 / ±21 萬8/4 bytes貨幣專用、隱含 4 位小數
近似數值FLOAT(n)-1.79E+308~1.79E+3084/8 bytes科學計算、不精確
REAL-3.40E+38~3.40E+384 bytesFLOAT(24)
日期時間DATETIME1753-01-01~9999-12-318 bytes精度 3.33 ms
SMALLDATETIME1900-01-01~2079-06-064 bytes精度 1 分鐘
DATETIME2(n)0001-01-01~9999-12-316~8 bytes精度 100ns、SQL 2008+ 推薦
DATE0001-01-01~9999-12-313 bytes僅日期、SQL 2008+
TIME(n)00:00:00~23:59:59.99999993~5 bytes僅時間、SQL 2008+
DATETIMEOFFSET(n)含時區8~10 bytes全球化應用
字串 (非 Unicode)CHAR(n)1~8,000 定長n bytes固定長度、填充空白
VARCHAR(n)1~8,000 變長實際長度+2 bytes通用字串
VARCHAR(MAX)2^31-1 bytes (2 GB)溢位儲存大型文字、取代 TEXT
字串 (Unicode)NCHAR(n)1~4,000 定長2n bytes固定長度、多語系
NVARCHAR(n)1~4,000 變長2×實際長度+2多語系通用
NVARCHAR(MAX)2^30-1 字元 (1 GB)溢位儲存大型 Unicode、取代 NTEXT
二進位BINARY(n)1~8,000 定長n bytes
VARBINARY(n)1~8,000 變長實際+2 bytes
VARBINARY(MAX)2 GB溢位取代 IMAGE
特殊UNIQUEIDENTIFIER16 bytes (GUID)16 bytesNEWID()/NEWSEQUENTIALID()
特殊TIMESTAMP / ROWVERSION8 bytes 遞增8 bytes樂觀鎖、版本戳、非日期
特殊CURSOR游標參考僅變數/參數、不可建表
特殊TABLE表值參數預存程序參數、UDT
特殊XML2 GB溢位結構化文件、支援索引/XQuery

特殊字元處理:錯誤示範 vs 正確姿勢

❌ 錯誤:字串拼接 (Classic ASP/VBScript 風格)

Function SQLFixup(TextIn)
    SQLFixup = Replace(TextIn, "'", "''")  ' 單引號轉義
End Function
' 使用
SQL = "SELECT * FROM Users WHERE Name = '" & SQLFixup(txtName) & "'"

缺陷: 僅處理單引號,無法防禦 `--`、`;`、`/* */`、Unicode 繞過、二階注入;維護成本高、易遺漏。

✅ 正確:參數化查詢 (唯一根治方案)

-- ADO.NET (C#)
using (var cmd = new SqlCommand("SELECT * FROM Users WHERE Name = @Name", conn))
{
    cmd.Parameters.Add("@Name", SqlDbType.NVarChar, 100).Value = txtName.Text;
    using (var rd = cmd.ExecuteReader()) { ... }
}

-- ADO Classic (ASP/VBScript) - 亦支援參數化
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = conn
cmd.CommandText = "SELECT * FROM Users WHERE Name = ?"
cmd.Parameters.Append cmd.CreateParameter("@Name", 202, 1, 100, txtName) ' adVarWChar
Set rs = cmd.Execute

⚠️ 動態 SQL 必須參數化

-- 預存程序內動態 SQL
CREATE PROC usp_Search @Name NVARCHAR(100), @SortCol SYSNAME
AS
BEGIN
    DECLARE @sql NVARCHAR(MAX) = N'SELECT * FROM Users WHERE Name = @Name';
    -- 排序欄位白名單驗證,不可參數化
    IF @SortCol NOT IN (N'Name', N'CreateDate', N'Status') SET @SortCol = N'Name';
    SET @sql += N' ORDER BY ' + QUOTENAME(@SortCol);
    EXEC sp_executesql @sql, N'@Name NVARCHAR(100)', @Name = @Name;
END

資料型別轉換與隱性轉換陷阱

-- 隱性轉換導致索引失效
WHERE VarcharCol = 123          -- 字串隱性轉數字,掃描全表
WHERE IntCol = '123'            -- 數字隱性轉字串,可能求值
-- 正確:顯性轉換或參數型別匹配
WHERE VarcharCol = CAST(123 AS VARCHAR(10))
WHERE IntCol = 123

特殊字元在 LIKE/全文檢索中的處理

-- LIKE 萬用字元轉義
WHERE Name LIKE '%[' + @Search + '%' ESCAPE '\'  -- 使用 ESCAPE 字元
-- 或動態建構
DECLARE @pattern NVARCHAR(100) = REPLACE(REPLACE(@Search, '[', '[[]'), '%', '[%]');
WHERE Name LIKE @pattern ESCAPE '\';

總結:防禦清單

  • [ ] **所有** 使用者輸入進入 SQL **必須參數化** (含動態 SQL 用 `sp_executesql` + 參數)。
  • [ ] 欄位型別與參數型別**嚴格匹配**,避免隱性轉換。
  • [ ] 動態識別子 (表名、欄位名) **白名單驗證** + `QUOTENAME()`。
  • [ ] 資料庫帳號**最小權限**,Web 應用無 DDL/系統權限。
  • [ ] 錯誤訊息**不回傳給前端**,記錄至後端日誌。
  • [ ] 定期代碼掃描:搜尋 `EXEC(`, `sp_executesql`, `+ @var +`, `QUOTENAME` 缺失等模式。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved