[Util]打擊 SQL Injection–Order By 篇

[Util]打擊 SQL Injection–Order By 篇

今天發現一支很有趣的程式

   1:  string sql = "SELECT * FROM CUST WHERE CUST_NAME Like @NAME";
   2:  if(!string.IsNullOrEmpty(sort))
   3:      sql = string.Concat(sql, "ORDER BY IP_ID");
   4:  // ...
   5:  command.Parameters.AddWithValue("@CUST_NAME", "%" + cust_name + "%" );
   6:   

第5行符合正規 SQL Injection 教戰手冊,

But 第3行好像讓一切努力付諸流水 (煙)

 

自己試了一下, 原來 Parameters 是不能用來做 Order By 傳遞的 (驚)

網路上有很多解法, 我這回實作的是Safe Order By, 引用 Regex,

將 sortExpression 送進一個filter的方法 SafeOrderBy( ),

而方法的核心就是這段 Regex

((,)?(\s)*\b(IP_ID|CUST_NAME)\b(\s)*(\b(ASC|DESC)(,)?\b)?)+

完整的內容如下:

 
            public string SafeOrderBy(string sortExpression, params string[] allow)
            {
                StringBuilder result = new StringBuilder();
                if (string.IsNullOrEmpty(sortExpression))
                {
                    return string.Empty;
                }
 
                if (allow == null || allow.Count() == 0)
                {
                    return string.Empty;
                }
 
                StringBuilder pattern = new StringBuilder(@"((,)?(\s)*\b(");
                foreach (string s in allow)
                {
                    pattern.Append(s).Append("|");
                }
                pattern.Remove(pattern.Length - 1, 1);
                pattern.Append(@")\b(\s)*(\b(ASC|DESC)(,)?\b)?)+");
 
                foreach (Match match in Regex.Matches(sortExpression, pattern.ToString()))
                {
                    if (match.Success)
                    {
                        result.Append(match.Value);
                    }
                }
                return result.ToString();
            }

 

套一句 91哥常說的 : 只允許朕指定的欄位,朕沒給的,你不能要 XD

 

 

 

 

單元測試 (充版面?!)

        [TestMethod()]
        public void TestSafeOrderBy()
        {
            SQL_CRM_CUST_AO_DAO target = new SQL_CRM_CUST_AO_DAO();
            string expected = "IP_ID";
            string actual = target.Util.SafeOrderBy("IP_ID", "IP_ID");
            Assert.AreEqual(expected, actual);
 
            expected = "";
            actual = target.Util.SafeOrderBy("CUST_NAME", "IP_ID");
            Assert.AreEqual(expected, actual);
 
            expected = "IP_ID DESC";
            actual = target.Util.SafeOrderBy("IP_ID DESC", "IP_ID", "CUST_NAME");
            Assert.AreEqual(expected, actual);
 
            expected = "IP_ID ASC";
            actual = target.Util.SafeOrderBy("IP_ID ASC", "IP_ID", "CUST_NAME");
            Assert.AreEqual(expected, actual);
 
            expected = "";
            actual = target.Util.SafeOrderBy("IP_IDX", "IP_ID");
            Assert.AreEqual(expected, actual);
 
            expected = "IP_ID ";
            actual = target.Util.SafeOrderBy("IP_ID ASCX", "IP_ID");
            Assert.AreEqual(expected, actual);
 
            expected = "";
            actual = target.Util.SafeOrderBy("XIP_ID", "IP_ID");
            Assert.AreEqual(expected, actual);
 
            expected = "IP_ID ";
            actual = target.Util.SafeOrderBy("IP_ID XASC", "IP_ID");
            Assert.AreEqual(expected, actual);
 
            expected = "IP_ID , CUST_NAME";
            actual = target.Util.SafeOrderBy("IP_ID , CUST_NAME", "IP_ID", "CUST_NAME");
            Assert.AreEqual(expected, actual);
 
            expected = "IP_ID , CUST_NAME";
            actual = target.Util.SafeOrderBy("IP_ID , CUST_NAME, MOBILE_TEL", "IP_ID", "CUST_NAME");
            Assert.AreEqual(expected, actual);
 
            expected = "IP_ID,CUST_NAME";
            actual = target.Util.SafeOrderBy("IP_ID,CUST_NAME", "IP_ID", "CUST_NAME");
            Assert.AreEqual(expected, actual);
 
            expected = "IP_ID DESC,CUST_NAME";
            actual = target.Util.SafeOrderBy("IP_ID DESC,CUST_NAME", "IP_ID", "CUST_NAME");
            Assert.AreEqual(expected, actual);
 
            expected = "IP_ID DESC,CUST_NAME ASC";
            actual = target.Util.SafeOrderBy("IP_ID DESC,CUST_NAME ASC", "IP_ID", "CUST_NAME");
            Assert.AreEqual(expected, actual);
        }