在最近的工作中,由於歷史遺留,一個分頁查詢沒有參數化,被查出來有sql注入危險,所以對這個查詢進行了參數化修改。
一看不知道,看了嚇一跳,可能由於種種原因,分頁查詢sql是在存儲過程中拼接出來的,where之后的條件也是在代碼中先進行拼接,然后作為整體參數在傳入存儲過程里,在存入過程里又進行一次拼接。這樣的話就有sql注入的潛在危險,盡管在拼接where之前進行的查詢條件的驗證。
大家都明白,參數化是防止sql注入的有效方法,然后就對這個分頁查詢進行大刀闊斧的改革。
思路一:1、對原先的代碼中拼接的where條件,不進行直接的賦值拼接。而是拼接成帶@符號的參數。並且給參數賦值;例如:
if (Num > 0) { sbWhere.Append(" AND s.SysNo=").Append(Num.Value); } 改 if (Num > 0) { sbWhere.Append(" AND s.SysNo = @SysNo "); paras.Add(new MySqlParameter("@SysNo", Num.Value)); }
2、給存儲過程添加參數
3、用SetParameters(paras.ToArray())方法直接把參數paras傳給存儲過程
結論:根本走不通,因為我們的查詢條件是動態拼接的,沒辦法動態給存儲過程定義傳入參數,這個思路直接給pass掉了;
小結:雖然這個方法走不通,但是在這個思路的第一步,是我們可以繼續用的,這個是沒有問題的。既然沒有辦法動態定義存儲過程參數,我們就不能再存儲過程中拼接分頁sql,只能把分頁sql的select和table、order都放到代碼程序中進行拼接,那么我們的思路二就來了。
思路二:1、接着我們思路一第一步之后的往下走
2、代碼中拼接分頁查詢sql;看代碼如下:
pagesb.AppendFormat("SELECT {0} from {1} where 1=1 {2} order by {3} limit (@pageNum - 1) * @pageSize,@pageSize;", selectField, tableName, whereSql, orderField);
結論:還是走不通啊啊啊啊啊啊!從錯誤日志中查看(@pageNum - 1) * @pageSize,@pageSize這些值都已經傳入了,但是就是報錯;內容如下:Message: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(1-1)*20, 20' at line 12.
最后發現limit(,) 函數參數內不能進行計算,只能傳入值,並且是int類型。
小結:我們就把(@pageNum - 1) * @pageSize的計算放到了代碼中。最后代碼如下:
int pageindex = (pageNum - 1) * pageSize; paras.Add(new MySqlParameter("@pageSize", pageSize)); paras.Add(new MySqlParameter("@pageindex", pageindex)); StringBuilder pagesb = new StringBuilder(); pagesb.AppendFormat("SELECT {0} from {1} where 1=1 {2} order by {3} limit @pageindex,@pageSize;", selectField, tableName, whereSql, orderField);
這樣我已經成功了一半,為什么說成功了一半呢?因為有些查詢條件出現了問題,像模糊查詢,怎么都查不出來數據,即時你輸入的查詢條件是可用的。費了九牛二虎之力找到了問題所在,我們的模糊查詢盡管改成了參數化,但是我們把@XXX參數沒替換成他對應的值,因為@XXX變成了一個字符串了代碼如下:
if (!string.IsNullOrEmpty(Contact)) { sbWhere.AppendFormat(" AND s.Contact LIKE '%{0}%'", @Contact"); paras.Add(new MySqlParameter("@Contact", Contact)); }
這個問題我們用mysql的CONCAT()函數進行處理,改之后代碼如下:
if (!string.IsNullOrEmpty(Contact)) { sbWhere.AppendFormat(" AND s.Contact LIKE CONCAT('%',{0},'%')", "@Contact"); paras.Add(new MySqlParameter("@Contact", Contact)); }
這樣我們就大功告成了。雖然性能上差了很多,但是這個主要是對參數化進行的更改,其中limit(,) 和CONCAT() 函數的使用比較重要。
再有就是本人也是初次接觸mysql,許多東西還需要學習。再有就是在給大家提個醒,關於IN() 函數的使用,如果有多個值1,2,3參數化類型會是string,替換值的時候會給‘1,2,3‘ 帶上單引號,自然也就找不到你需要的數據了,所以還是用or 慢慢去拼接吧。
鄭重聲明:本博客,只是為了參數化,性能方面沒有過多的考慮。如有錯誤之處請大家海涵,望多多給予指點。