這邊要討論的不是一般的sql injection,而是在LIKE pattern中出現的情況。
我學到的教訓:即使使用了參數化查詢,仍然需要處理 LIKE 的特殊字元,例如 %、_ 與 [
情境:實作搜尋功能
在撰寫搜尋功能時,我們常會使用 LIKE 搭配萬用字元 % 來做模糊比對。例如:
WHERE MediaName LIKE @UserInput並在Csharp中將參數設為:
var userInput = ... // 取得使用者的輸入
parameters.Add("UserInput", $"%{userInput}%");表面上看起來這是安全的做法,因為使用了參數化查詢,可以避免傳統的 SQL Injection。
LIKE 的特殊字元
在 SQL Server 裡:
| 字元 | 意義 |
|---|---|
% | 任意長度字串 |
_ | 任意一個字元 |
[ ] | 字元集合 |
但如果使用者想輸入的影音真的就叫做%,因此輸入 %查詢,實際送進資料庫的條件就會變成: |
WHERE MediaName LIKE '%'` 時,資料庫會發現這個條件幾乎等於沒有條件。既然所有資料都需要被讀取,那麼使用索引來定位特定範圍就沒有意義,最合理的執行策略就是直接掃描整張資料表,也就是所謂的全表掃描。
## 解決方法
為了避免使用者透過這些特殊字元影響查詢邏輯,可以在送入資料庫前,先對字串進行跳脫處理。以下是一個常見做法:
```csharp
private string EscapeLikePattern(string input)
{
if (string.IsNullOrEmpty(input)) return input;
return input
.Replace("[", "[[]")
.Replace("%", "[%]")
.Replace("_", "[_]");
}在組合參數時,先將使用者輸入經過這個方法處理,再加入萬用字元:
var userInput = ... // 取得使用者的輸入
var escaped = EscapeLikePattern(userInput);
parameters.Add("UserInput", $"%{escaped}%");這樣做之後,使用者即使輸入 % 或 _,資料庫也只會把它們當成普通字元比對,而不會改變搜尋語意。如此一來,可以避免查詢退化成全表掃描,同時讓搜尋行為更可預期。