其實對與初學者來說,進行的動態的查詢語句拼接也不是那麼好做的事情,就是做出來了,也未必是經得起考驗的足夠靈活好用的,未必是能拿得出手可以進行
推廣的,是否能拿得出就是其中的關鍵。
今天檢查公司的軟件項目質量,發現有2個同事寫的程序存在SQL注入攻擊的漏洞,當然也不能怪罪人家,他們也是剛參加工作1-2年,還沒有那麼豐富的技術經驗、安全意識,不會注意到自己編寫的系統會有嚴重的安全漏洞。
公司在寧波也有一個政府網站的項目,在做系統安全檢查自動掃描時,也被查出了一些SQL注入攻擊的漏洞,這也使得讓我更加提高了系統是否存在安全漏洞的意識,當然我寫的系統很少會有這類的漏洞,但是整個公司有幾十號人在做開發公司,所以也難免別人不會犯錯。
人工檢查是否有SQL注入安全漏洞,其實非常簡單,只要在查詢輸入框裡輸入有單引號的字符“'”,例如查詢條件輸入“吉日'嘎拉”其中有單引號,很可能這個查詢的頁面就崩潰了出現了錯誤頁面,或者跳轉到出錯頁面了,若有這樣的情況發生,幾乎99.3721%都可以看做是有潛在的SQL注入攻擊漏洞。
防止這個錯誤的發生,從技術上應該說是要用“參數化的查詢”,貼一些代碼,給大家參考一下我的做法:
代碼
#region public DataTable Search(string folderId, string searchValue, Boolean deleteMark) 查詢
/// <summary>
/// 查詢
/// </summary>
/// <param name="folderId">目錄</param>
/// <param name="searchValue">查詢條件</param>
/// <param name="deleteMark">刪除標志</param>
/// <returns>數據表</returns>
public DataTable Search(string folderId, string searchValue, Boolean deleteMark)
{
// 一、這裡是將Boolean值轉換為int類型。
int delete = deleteMark ? 1: 0;
// 二、這裡是開始進行動態SQL語句拼接,字段名、表明都進行了常量定義,表名字段名發生變化時,很容易就知道程序哪裡都調用了這些。
string
sqlQuery = string.Empty;
sqlQuery = " SELECT " + BaseNewsTable.FIEldId
+ " ," + BaseNewsTable.FIEldFolderId
+ " ," + BaseNewsTable.FIEldTitle
+ " ," + BaseNewsTable.FIEldFilePath
+ " ," + BaseNewsTable.FIEldFileSize
+ " ," + BaseNewsTable.FIEldReadCount
+ " ," + BaseNewsTable.FIEldDe
scription
+ " ," + BaseNewsTable.FIEldCategoryCode
+ " ," + BaseNewsTable.FIEldEnabled
+ " ," + BaseNewsTable.FIEldDeleteMark
+ " ," + BaseNewsTable.FIEldSortCode
+ " ," + BaseNewsTable.FIEldCreateUserId
+ " ," + BaseNewsTable.FIEldCreateUserRealname
+ " ," + BaseNewsTable.FIEldCreateDate
+ " ," + BaseNewsTable.FIEldModifyUserId
+ " ," + BaseNewsTable.FIEldModifyUserRealname
+ " ," + BaseNewsTable.FIEldModifyDate
+ " FROM " + this.CurrentTableName
+ " WHERE " + BaseNewsTable.FIEldDeleteMark + " = " + delete;
// 三、我們認為 folderId 這個查詢條件是安全,不是人為輸入的參數,所以直接進行了SQL語句拼接
if (!String.IsNullOrEmpty(folderId))
{
sqlQuery += " AND " + BaseNewsTable.FIEldFolderId + " = '" + folderId + "'";
}
// 四、這裡是進行參數化的准備,因為是多個不確定的查詢參數,所以用了List。
List<DbParameter> dbParameters = new List<DbParameter>();
// 五、這裡看查詢條件是否為空
searchValue = searchValue.Trim();
if (!String.IsNullOrEmpty(searchValue))
{
// 六、這裡是進行支持多種
數據庫的參數化查詢
sqlQuery += " AND (" + BaseNewsTable.FIEldTitle + " LIKE " + DbHelper.GetParameter(BaseNewsTable.FIEldTitle);
sqlQuery += " OR " + BaseNewsTable.FIEldCreateUserRealname + " LIKE " + DbHelper.GetParameter(BaseNewsTable.FIEldCreateUserRealname);
sqlQuery += " OR " + BaseNewsTable.FIEldContents + " LIKE " + DbHelper.GetParameter(BaseNewsTable.FIEldContents);
sqlQuery += " OR " + BaseNewsTable.FIEldDescription + " LIKE " + DbHelper.GetParameter(BaseNewsTable.FIEldDescription) + ")";
// 七、這裡是判斷,用戶是否已經輸入了%
if (searchValue.IndexOf("%") < 0)
{
searchValue = "%" + searchValue + "%";
}
// 八、這裡生成支持多數據庫的參數
dbParameters.Add(DbHelper.MakeInParam(BaseNewsTable.FIEldTitle, searchValue));
dbParameters.Add(DbHelper.MakeInParam(BaseNewsTable.FIEldCreateUserRealname, searchValue));
dbParameters.Add(DbHelper.MakeInParam(BaseNewsTable.FIEldContents, searchValue));
dbParameters.Add(DbHelper.MakeInParam(BaseNewsTable.FIEldDescription, searchValue));
}
// 九、這裡是將List轉換為
數組,進行數據庫查詢
return DbHelper.Fill(sqlQuery, dbParameters.ToArray());
}
#endregion
對以上代碼進行一下簡單的點評:
優點有如下:
1:支持多數據庫。
2:dbParameters.Add,非常靈活,可以動態組織多個查詢條件條件,想要幾個參數就要幾個參數,最後又巧妙的用了 dbParameters.ToArray(),這個是值得學習的。
3:函數的命名、注釋、參數的大小寫比較規范。
4:函數的注釋寫得很棒,對初學者學習模仿起了一個好榜樣。
5:searchValue.IndexOf("%") 也想得比較深入,有些專業人事查詢時,可能已經輸入了%。
6:表名、表字段都進行了定義,若後期表名、表字段發生了變化,程序不會有未知的錯誤,很容易在編碼階段就發現錯誤,在軟件開發初期,表名、字段名變來變去是很常見的事情,若是好幾個人開發的,又經常變來變去的,若是當時沒控制好,後期就會亂套。
7:SQL語句拼接時,沒有用 WHERE 1=1 AND , 而是巧妙的運用了 DeleteMark 字段,是值得學習的。
8:SQL語句中關鍵字都進行了大寫,是比較符合行業規范的,大家也值得參考。
9:不管如何,對自己寫的每行代碼,每行注釋,都有了深入的講解、深入的認識,這是大家值得學習的,若哪裡寫得不好,有錯就改的精神,分享的精神是值得學習的。
10:先關注好身邊的事情、才能關注網上的事情,是大家值得學習的,別總是有過於遠大的理想,過於理想化的夢想,把身邊的事情都做好,把身邊的同事都關注好,就是干得最出色最及格了,這個務實的精神也是大家值得學習的。
11: 查詢代碼思路嚴謹得有些過分,實在是過分,區區一個動態查詢居然分了9個步驟來實現,思路細膩嚴謹得讓人實在是佩服啊,不佩服不行啊,菜鳥程序員的驕傲、學習模仿的楷模,呵呵。
先就寫到這裡吧,再誇下去,估計很多人都要吐了。
相應的源碼給大家敬上,若能起到參考,我也很榮幸了
/Files/jirigala/DotNet.CommonV3.0.rar
/Files/jirigala/handbookV3.0.pdf