雖然現在遇到使用者輸入條件查詢DB,我一律都用參數化查詢(順推超好用的Dapper)不再偷懶組裝SQL指令,但關於SQL Injection,我心中始終藏著一個疑問:流傳千古的… WHERE Col = '" + input.Replace("'", "''") + "'"換單引號大法,人人都知它不夠安全,但網路流傳一種說法,指稱換單引號只是自欺欺人根本無效,奇怪的是卻很少看到它被打穿的實例。我完全同意置換法不夠安全,黑名單法一旦漏列就會破功,但既然要把它說得不堪一擊,拿點證據出來很難嗎?很難嗎?很難嗎?

我試了,還真的不簡單…

爬文找到幾種主張置換法無效的說法:

  • 遇到WHERE NumberCol = " + input.Replace("'", "''")時置換無效
    如果欄位是數字型別,將input字串轉成數字才嚴謹,寫成這様是自己跳坑,不列入考慮
  • 利用編碼轉換偷渡單引號
    字串置換函式、資料庫驅動程式或資料庫採用非Unicode編碼,透過巧妙安排讓傳入的Unicode字串轉換後形成單引號。若以SQL Server為例,Client與Server幾乎都已是Unicode,以C# System.String.Replace()、內建SqlClient程式庫、SQL2005-2012為例,置換單引號的做法不致留下轉碼漏洞(參考),但在其他環境則很難說。
  • 利用單引號等效字元
    爬文發現一種傳說中的神奇字元0x02bc(Modifier Letter Apostrophe),串接在SQL指令效果等同單引號。這讓我為之一震,可以替代單引號的神祕字元?好一隻黑天鵝

眾多說法中最吸引我目光的莫過0x02bc字元,尤其不少人說這招在SQL Server及MySQL都管用,甚至有範例程式:參考

string badValue = ((char)0x02BC).ToString();
badValue = badValue + ";delete from widgets--";
string sql = "SELECT * FROM WIDGETS WHERE ID=" + badValue.Replace("'","''");
TestTheSQL(sql);

迫不及待想驗證,很可惜,對SQL Server Express 2014做了類似測試,攻擊失敗!0x02bc被視為一般字元,安全過關。

如果SQL不行,那MySQL呢?特地安裝了MySQL 5.7 Community,如法泡製拔出0x02bc進攻,再次鎩羽而歸。

由以上實測,至少證明0x02bc已不具備打穿SQL Server Express 2014跟MySQL 5.7的神奇功效。或許在以前的某些SQL或MySQL版本上是可行的,但至少在我測的兩種DB版本已不存在。無心再去找活化石老DB試玩,就此打住。

意思是我們可以安心繼續組SQL字串再用單引號置換法嗎?當然不是,遇上特定資料庫、程式庫版本,只置換單引號必然存在等效特殊字元偷渡做亂的可能性,無論換掉多少可疑的字元,只要有漏網之魚就會破功,這是黑名單法無法被補強的本質缺陷。所以,乖乖使用參數化查詢,不要組裝SQL指令才是治本之道!

【結論】我百分之百贊同「置換單引號不能杜絕SQL Injection,在某些特殊情境下會被攻破」,但在沒有強力佐證的情況下要說「哼!換掉單引號根本沒屁用」則略嫌浮誇。

以上看法屬爬文及實驗後的心得,受限個人所見所學,就當拋磚引玉吧,以下開放想打臉、補刀的朋友提出實證討論。(我真的好想看到單引號置換法被打穿)


Comments

# by 可可

用LINQ不就好了?

# by mis2000lab

Q : 用LINQ不就好了? 我喜歡黑暗執行緒這篇文章(http://blog.darkthread.net/post-2008-05-14-transaction-in-linq-to-sql.aspx)結尾"最後兩段"的說法。 :-)

# by 霧隱虎

Dear 黑大: 有使用偏門資料庫FireBird,另外附上測試資料與測試DB,無 Injection問題

# by 霧隱虎

https://github.com/kirigakuretora/KirigakuretoraBlogExampleFireBird/tree/master/KirigakuretoraProjectsHexadecimal1st

# by Jeffrey

to 霧隱虎,感謝測試,0x02bc字元目前戰績:0勝3敗。

# by OXXO

LINQ不是萬能,有時你還要用的組SQL字串才能WORK

Post a comment