本文摘自:https://blog.csdn.net/qian_qian_123/article/details/92844194
※:PreparedStatement不允許在插入?yún)?shù)時(shí)改變SQL語(yǔ)句的邏輯結(jié)構(gòu)俱饿。
※:為什么它這樣處理就能預(yù)防SQL注入提高安全性呢暇咆?其實(shí)是因?yàn)镾QL語(yǔ)句在程序運(yùn)行前已經(jīng)進(jìn)行了預(yù)編譯,在程序運(yùn)行時(shí)第一次操作數(shù)據(jù)庫(kù)之前云矫,SQL語(yǔ)句已經(jīng)被數(shù)據(jù)庫(kù)分析抒和,編譯和優(yōu)化矫渔,對(duì)應(yīng)的執(zhí)行計(jì)劃也會(huì)緩存下來(lái)并允許數(shù)據(jù)庫(kù)已參數(shù)化的形式進(jìn)行查詢,當(dāng)運(yùn)行時(shí)動(dòng)態(tài)地把參數(shù)傳給PreprareStatement時(shí)构诚,即使參數(shù)里有敏感字符如 or '1=1'也數(shù)據(jù)庫(kù)會(huì)作為一個(gè)參數(shù)一個(gè)字段的屬性值來(lái)處理而不會(huì)作為一個(gè)SQL指令蚌斩,如此,就起到了防止SQL注入的作用了范嘱!
①
經(jīng)常碰到這樣的面試題目:#{}和${}的區(qū)別是什么送膳?
網(wǎng)上的答案是:#{}是預(yù)編譯處理,$ {}是字符串替換丑蛤。mybatis在處理#{}時(shí)叠聋,會(huì)將sql中的#{}替換為?號(hào),調(diào)用PreparedStatement的set方法來(lái)賦值受裹;mybatis在處理 $ { } 時(shí)碌补,就是把 ${ } 替換成變量的值。使用 #{} 可以有效的防止SQL注入棉饶,提高系統(tǒng)安全性厦章。
對(duì)于這個(gè)題目我感覺(jué)要抓住兩點(diǎn):
(1)$ 符號(hào)一般用來(lái)當(dāng)作占位符,常使用Linux腳本的人應(yīng)該對(duì)此有更深的體會(huì)吧照藻。既然是占位符袜啃,當(dāng)然就是被用來(lái)替換的。知道了這點(diǎn)就能很容易區(qū)分$和#幸缕,從而不容易記錯(cuò)了群发。
(2)預(yù)編譯的機(jī)制晰韵。預(yù)編譯是提前對(duì)SQL語(yǔ)句進(jìn)行預(yù)編譯,而其后注入的參數(shù)將不會(huì)再進(jìn)行SQL編譯熟妓。我們知道雪猪,SQL注入是發(fā)生在編譯的過(guò)程中,因?yàn)閻阂庾⑷肓四承┨厥庾址鹩詈蟊痪幾g成了惡意的執(zhí)行操作只恨。而預(yù)編譯機(jī)制則可以很好的防止SQL注入。
最后想說(shuō)的是抬虽,對(duì)于mybatis 以及 sql 而言坤次,每一個(gè)考點(diǎn)背后都是有一個(gè)深刻的思想存在的,應(yīng)該好好的體會(huì)斥赋。這樣才能真正的做到技術(shù)提升缰猴,成為技術(shù)大牛。
②
${param}傳遞的參數(shù)會(huì)被當(dāng)成sql語(yǔ)句中的一部分疤剑,比如傳遞表名滑绒,字段名
例子:(傳入值為id)
orderby${param}
則解析成的sql為:
orderbyid
#{parm}傳入的數(shù)據(jù)都當(dāng)成一個(gè)字符串,會(huì)對(duì)自動(dòng)傳入的數(shù)據(jù)加一個(gè)雙引號(hào)
例子:(傳入值為id)
select*fromtablewherename =#{param}
則解析成的sql為:
select*fromtablewherename ="id"
為了安全隘膘,能用#的地方就用#方式傳參疑故,這樣可以有效的防止sql注入攻擊
sql注入簡(jiǎn)介
直接上了百度的例子,感覺(jué)一看就清晰明了
某個(gè)網(wǎng)站的登錄驗(yàn)證的SQL查詢代碼為:
strSQL?="SELECT?*?FROM?users?WHERE?(name?=?'"+?userName?+"')?and?(pw?=?'"+?passWord?+"');"
惡意填入
userName?="1'?OR?'1'='1";
與passWord?="1'?OR?'1'='1";
時(shí)弯菊,將導(dǎo)致原本的SQL字符串被填為
strSQL?="SELECT?*?FROM?users?WHERE?(name?=?'1'?OR?'1'='1')?and?(pw?=?'1'?OR?'1'='1');"
也就是實(shí)際上運(yùn)行的SQL命令會(huì)變成下面這樣的
strSQL?="SELECT?*?FROM?users;"
這樣在后臺(tái)帳號(hào)驗(yàn)證的時(shí)候巧妙地繞過(guò)了檢驗(yàn)纵势,達(dá)到無(wú)賬號(hào)密碼,亦可登錄網(wǎng)站管钳。所以SQL注入攻擊被俗稱為黑客的填空游戲钦铁。
③
動(dòng)態(tài) sql 是 mybatis 的主要特性之一,在 mapper 中定義的參數(shù)傳到 xml 中之后才漆,在查詢之前 mybatis 會(huì)對(duì)其進(jìn)行動(dòng)態(tài)解析牛曹。mybatis 為我們提供了兩種支持動(dòng)態(tài) sql 的語(yǔ)法:#{} 以及 ${}。
在下面的語(yǔ)句中醇滥,如果 username 的值為 zhangsan黎比,則兩種方式無(wú)任何區(qū)別:
select * from user where name = #{name};
select * from user where name = ${name};
其解析之后的結(jié)果均為
select * from user where name = 'zhangsan';
但是 #{} 和 ${} 在預(yù)編譯中的處理是不一樣的。#{} 在預(yù)處理時(shí)鸳玩,會(huì)把參數(shù)部分用一個(gè)占位符 ? 代替阅虫,變成如下的 sql 語(yǔ)句:
select * from user where name = ?;
而 ${} 則只是簡(jiǎn)單的字符串替換,在動(dòng)態(tài)解析階段不跟,該 sql 語(yǔ)句會(huì)被解析成
select * from user where name = 'zhangsan';
以上颓帝,#{} 的參數(shù)替換是發(fā)生在 DBMS 中,而 ${} 則發(fā)生在動(dòng)態(tài)解析過(guò)程中。
那么躲履,在使用過(guò)程中我們應(yīng)該使用哪種方式呢?
答案是聊闯,優(yōu)先使用 #{}工猜。因?yàn)?${} 會(huì)導(dǎo)致 sql 注入的問(wèn)題×馐撸看下面的例子:
select * from ${tableName} where name = #{name}
在這個(gè)例子中篷帅,如果表名為
?user; delete user; --?
則動(dòng)態(tài)解析之后 sql 如下:
select * from user; delete user; -- where name = ?;
--之后的語(yǔ)句被注釋掉,而原本查詢用戶的語(yǔ)句變成了查詢所有用戶信息+刪除用戶表的語(yǔ)句拴泌,會(huì)對(duì)數(shù)據(jù)庫(kù)造成重大損傷魏身,極大可能導(dǎo)致服務(wù)器宕機(jī)。
但是表名用參數(shù)傳遞進(jìn)來(lái)的時(shí)候蚪腐,只能使用 ${} 箭昵,具體原因可以自己做個(gè)猜測(cè),去驗(yàn)證回季。這也提醒我們?cè)谶@種用法中要小心sql注入的問(wèn)題家制。