SQL注入的實質就是通過SQL拼接字符串追加命令,導致SQL的語義發生了變化。為什么發生了改變呢? 因為沒有重用以前的執行計划,而是對注入后的SQL語句重新編譯,然后重新執行了語法解析。 所以要保證SQL語義不變,(即想要表達SQL本身的語義,並不是注入后的語義)就必須保證執行計划確定不被改變。
SQL注入的表現
示例
1.我們先熟悉一下表里的東西
2.要查詢的原SQL語句。

3.注入的SQL語句

分析示例:
原SQL語句
select COUNT(*) from T_Users where UserName = 'a' and Password ='b'1
語義:要查找(用戶名為‘a’,密碼為‘b’) 的用戶總共有幾個. 查詢結果為1
注入后的SQL語句
select COUNT(*) from T_Users where UserName = 'a' and Password = 'b' or 1=1 --'1
注入后的語義:查找 (密碼是a的,並且用戶名是b的,) 或者1=1 的所有用戶的數量。 這時候語義發生了改變,查詢結果為4
分析:如果用戶在輸入密碼時, 輸入的不是b 而是b’ or 1=1- - ,語義將會發生改變,(- -在數據庫中是注釋的標記)最后查詢的結果當然也就不相同了。原語句的查詢結果為1,注入后的查詢結果為4。
SQL注入的危害
我們剛剛示范了SQL注入帶來的查詢結果的改變,這個可能會讓別有用心的用戶熟知我們數據庫中的記錄條數。或者如果輸入的是b’ delect T_Users - - 這時候我們整張表都被刪除了,這個帶來的后果可想而知。
防止SQL注入–參數化查詢
我們的機房重構中D層一直在用參數化查詢,但是我當時沒有完全理解用參數化查詢的用意。直到現在才理解了為什么要用參數化查詢和參數化查詢如何防止SQL注入的。
public int checkUser(Users user) { string sql = "select COUNT(*) from T_Users where UserName = @UserName and Password = @Password"; SqlParameter[] paras ={new SqlParameter ("@UserName",User.username) , new SqlParameter ("@Password",User.password)}; int num = sqlhelper.ExecuteNonQuery(sql, paras); return num ; }12345678
這是我們用了參數化查詢后的效果,我們來分析一下參數化查詢是如何防止sql注入的。
select count(*) from T_Users where UserName = ‘a’ and Password = ‘b’ or 1=1- -‘,
是等到 UserName 參數傳過來,才會去編譯這條 sql語句(sql語句是編譯后執行的),而這個時候, 這個拼接的sql 多了 or 選項,這改變了sql語句的本意。
是等到 UserName 參數傳過來,才會去編譯這條 sql語句(sql語句是編譯后執行的),而這個時候, 這個拼接的sql 多了 or 選項,這改變了sql語句的本意。
參數化查詢:是先編譯 這樣一條sql, select count (*) from T_Users where = UserName = @UserName and Password = @Password ,和函數的編譯一樣, 這樣的話,以后’b’ or 1=1- - ’ 只能作為 Password 的參數了,,,相當於你去查 ” ‘b’ or 1=1—’ ” 這個密碼,而不是 查 “b” 這個密碼 或者 ” 1=1 ” 了。 相當於把 or 給屏蔽掉了。or 原來是sql的一項(普通的拼接),現在就是個普通字符串參數(參數化查詢)
,不能改變sql語義,也不能改變編譯執行計划,所以防止了SQL注入。
,不能改變sql語義,也不能改變編譯執行計划,所以防止了SQL注入。
示例小結:
正常的命令是程序中的“常量”,能動手腳的只能是可輸入的用戶名、密碼這些可輸入的“變量”。 拼接方式是將常量、變量拼在一起編譯,才可以在正常的命令后追加命令。參數化的方式僅常量參與編譯,就無法追加命令,防止了sql注入。
總結:
參數化查詢的方式簡單來說就是指用參數參與編譯,這樣就全是常量了,編譯執行計划時沒有用到參數,所以命令是確定的。編譯后計划便確定了,編譯后傳參數知識為了運行執行計划,執行計划不變,就不會多出命令,SQL語義就無法改變,防止了sql注入。
