雖然現在遇到使用者輸入條件查詢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,在某些特殊情境下會被攻破」,但在沒有強力佐證的情況下要說「哼!換掉單引號根本沒屁用」則略嫌浮誇。
以上看法屬爬文及實驗後的心得,受限個人所見所學,就當拋磚引玉吧,以下開放想打臉、補刀的朋友提出實證討論。(我真的好想看到單引號置換法被打穿)