結(jié)合Mybatis聊聊對(duì)SQL注入的見(jiàn)解
Mybatis聊聊對(duì)SQL注入的見(jiàn)解
1.sql注入是什么
sql注入見(jiàn)名思意,是指一些非法用戶通過(guò)將一些特殊字符或者sql語(yǔ)句插入到要提交的表單之中,從而讓服務(wù)器在不知情的情況下執(zhí)行惡意的sql命令,從而引發(fā)一系列的安全隱患。
講的通俗一點(diǎn)就是說(shuō),用戶利用sql語(yǔ)法將一些sql語(yǔ)句加在某些字段后面,提交表單的時(shí)候,服務(wù)器執(zhí)行sql命令并未達(dá)到想要的結(jié)果反而引發(fā)異常和數(shù)據(jù)泄露。
這就是典型的系統(tǒng)漏洞,因此sql注入對(duì)系統(tǒng)的危害是非常大的,做好防止sql注入也是系統(tǒng)必須完善的。
2.sql注入實(shí)例
我寫(xiě)了個(gè)簡(jiǎn)單的登錄程序,框架是Spring+SpringMVC+Mybatis。表結(jié)構(gòu)如下:

(1)特殊符號(hào),如 ' 和 .等

可以看到當(dāng)我輸入一個(gè)特殊符號(hào),而后臺(tái)未經(jīng)過(guò)過(guò)濾的時(shí)候,便會(huì)拋出sql異常:

并且如果沒(méi)有進(jìn)行全局異常處理,用戶便可以通過(guò)瀏覽器的開(kāi)發(fā)者模式中獲取到數(shù)據(jù)庫(kù)和查詢語(yǔ)句的相關(guān)信息。

而這個(gè)對(duì)系統(tǒng)來(lái)說(shuō)是很危險(xiǎn)的漏洞。
(2)sql語(yǔ)句的注入

當(dāng)輸入正確的賬號(hào)密碼的時(shí)候,可以看到執(zhí)行時(shí)成功的。
但是如果加上這么一句sql語(yǔ)句在表單,我們可以看到結(jié)果依然可以執(zhí)行成功

非法用戶可以通過(guò)這個(gè)來(lái)測(cè)試數(shù)據(jù)庫(kù)的表名字、該表的其他字段名、數(shù)據(jù)的量級(jí)等等。
舉個(gè)例子:嘗試獲取到該表的其他字段名
只需要將剛才的表單中的“1=1”替換成測(cè)試sql語(yǔ)句即可,如果執(zhí)行成功則代表該表中有這個(gè)字段存在。

看一下最后執(zhí)行的sql語(yǔ)句:

就一目了然了。這通用是因?yàn)樽⑷胍鸬摹?/p>
類(lèi)似的還有:'or''=' 不僅能執(zhí)行成功,同時(shí)還有查詢多全部的數(shù)據(jù)。
(3)通過(guò)sql語(yǔ)句的in和order by進(jìn)行注入
以上的三點(diǎn)主要是因?yàn)樵趍ybatis中使用了${}, sql語(yǔ)句沒(méi)有執(zhí)行預(yù)編譯,無(wú)法防止sql注入。
3.mybatis框架下如何解決sql注入問(wèn)題
mybatis框架本身就有防止sql注入的特性,這就要求我們必須在寫(xiě)sql語(yǔ)句時(shí)候遵循框架的書(shū)寫(xiě)規(guī)范。
(1)用#{param}替換所有的${param}
因?yàn)?{}是拼接sql字符實(shí)現(xiàn)沒(méi)有預(yù)編譯的查詢,因?yàn)槭菬o(wú)法防御sql注入,而#{}則需要進(jìn)行預(yù)編譯,可以很大程度上防止sql注入。
在一些#{}使用時(shí)候會(huì)報(bào)錯(cuò)的地方,如like 查詢、in 查詢、order by排序等,
a) like:
select * from Users where username like '%${username}%'
替換成:
select * from Users where username like concat('%', #{username}, '%')
b) in
select * from Users where id in (${id})
替換成mybatis框架自帶的foreach循環(huán):

c) order by
不要直接使用:拼接排序
如:
select *from Users order by ${id}
而是在java層面做映射,然后用<if>來(lái)做判斷

其他的類(lèi)似的情況請(qǐng)按照相同的處理方式進(jìn)行處理即可。
(2)對(duì)用戶輸入的數(shù)據(jù)進(jìn)行sql注入校驗(yàn)
SqlServer本身的防sql注入機(jī)制,利用存儲(chǔ)過(guò)程可以避免sql注入。應(yīng)該禁止用戶輸入一些關(guān)鍵的特殊符號(hào),如分號(hào)、分隔符、單引號(hào)等,同時(shí)對(duì)于一些關(guān)鍵位置進(jìn)行sql關(guān)鍵字的屏蔽,如or、and等。必須對(duì)用戶輸入的內(nèi)容的類(lèi)型、長(zhǎng)度、格式、范圍進(jìn)行校驗(yàn)。
(3)要對(duì)用戶的權(quán)限進(jìn)行區(qū)分
普通用戶的權(quán)限和管理員的權(quán)限之間,必須嚴(yán)格區(qū)分開(kāi)來(lái),對(duì)管理員實(shí)現(xiàn)安全級(jí)別更高的驗(yàn)證,從而防止人為獲取到更高權(quán)限時(shí)候的sql注入攻擊。
(4)更高級(jí)別的驗(yàn)證
在后端代碼和數(shù)據(jù)庫(kù)中都開(kāi)啟對(duì)sql注入的驗(yàn)證,同時(shí)用專(zhuān)業(yè)的注入工具查找本系統(tǒng)的漏洞進(jìn)行修復(fù),也可以進(jìn)行賬號(hào)誘騙,將一些如“admin”之類(lèi)的容易受到攻擊的用戶設(shè)置上千位的密碼,讓攻擊者的軟件因?yàn)榻馕隽看蠖?fù)載過(guò)大,從而耗盡資源而宕機(jī)。
一種常見(jiàn)的Mybatis的SQL注入
1.場(chǎng)景重現(xiàn)
現(xiàn)在有一張名為student的表,表結(jié)構(gòu)如下:

其中id為自增主鍵,余下字段分別為英語(yǔ)成績(jī)、數(shù)學(xué)成績(jī)、美術(shù)成績(jī)、學(xué)生的學(xué)號(hào)、學(xué)生的姓名、學(xué)生的電話。
目前表里面有6條數(shù)據(jù):

2.代碼展示
使用mybatis框架實(shí)現(xiàn)根據(jù)美術(shù)成績(jī)查找相應(yīng)的記錄,在mapper.xml文件里,代碼大概會(huì)這么寫(xiě):
<!--通過(guò)美術(shù)成績(jī)作為篩選條件查詢-->
<select id="queryByArtGrade" resultMap="StudentMap">
select
id, english_grade, math_grade, art_grade, number, name, telephone
from student
<where>
<if test="artGrade != null">
and art_grade = #{artGrade}
</if>
</where>
</select>
當(dāng)然也可以這么寫(xiě):
<!--通過(guò)美術(shù)成績(jī)作為篩選條件查詢-->
<select id="queryByArtGrade" resultMap="StudentMap">
select
id, english_grade, math_grade, art_grade, number, name, telephone
from student
<where>
<if test="artGrade != null">
and art_grade = ${artGrade}
</if>
</where>
</select>
這兩種寫(xiě)法的區(qū)別在于對(duì)傳入?yún)?shù)的接收方式不同,前者是#{property},后者是${property}。
3.模擬請(qǐng)求&回應(yīng)結(jié)果
想查詢美術(shù)成績(jī)?yōu)?0的記錄,使用postman進(jìn)行請(qǐng)求,傳入?yún)?shù)json為:
{
"artGrade":"70"
}
測(cè)試結(jié)果兩者均為:

看起來(lái)好像是一樣的結(jié)果,兩種寫(xiě)法都正確。
但是我把參數(shù)改成下面的這個(gè)樣子:
{
"artGrade":"'70' and id='4'"
}
結(jié)果就完全不同啦!
使用#{property}返回的結(jié)果為空:

通過(guò)日志,發(fā)現(xiàn)實(shí)際執(zhí)行的SQL為:
select id, english_grade, math_grade, art_grade, number, name, telephone from classroom.student WHERE art_grade = "'70' and id='4'"
使用${property}返回的結(jié)果為一條:

通過(guò)日志,發(fā)現(xiàn)實(shí)際執(zhí)行的SQL為:
select id, english_grade, math_grade, art_grade, number, name, telephone from classroom.student WHERE art_grade = '70' and id='4'
4.小結(jié)一下
這就是常見(jiàn)的sql注入了,因?yàn)楹蠖私邮諅魅雲(yún)?shù)的時(shí)候沒(méi)有經(jīng)過(guò)任何處理,直接到了mybatis層,而mybatis層接收參數(shù)的時(shí)候又使用了${property}這種方式,導(dǎo)致參數(shù)被直接拼接至預(yù)執(zhí)行的SQL中,最后返回了一些惡意請(qǐng)求者需要的信息。
所以,mapper中盡量避免使用${property}來(lái)接收參數(shù),因?yàn)槟悴恢郎弦粚觽鬟M(jìn)來(lái)的東西到底是什么。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
淺談Java分布式架構(gòu)下如何實(shí)現(xiàn)分布式鎖
這篇文章主要介紹了淺談Java分布式架構(gòu)下如何實(shí)現(xiàn)分布式鎖,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Netty分布式ByteBuf使用page級(jí)別的內(nèi)存分配解析
這篇文章主要介紹了Netty分布式ByteBuf使用page級(jí)別的內(nèi)存分配解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
Java中HTTP GET方法調(diào)用帶有body的問(wèn)題解決
這篇文章主要為大家詳細(xì)介紹了Java如何解決HTTP GET方法調(diào)用帶有body的問(wèn)題,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下2024-02-02
關(guān)于各種排列組合java算法實(shí)現(xiàn)方法
這篇文章介紹了幾種用JAVA實(shí)現(xiàn)的排列組合算法,有需要的朋友可以參考一下2013-06-06
Java使用DateUtils對(duì)日期進(jìn)行數(shù)學(xué)運(yùn)算經(jīng)典應(yīng)用示例【附DateUtils相關(guān)包文件下載】
這篇文章主要介紹了Java使用DateUtils對(duì)日期進(jìn)行數(shù)學(xué)運(yùn)算的方法,可實(shí)現(xiàn)針對(duì)日期時(shí)間的各種常見(jiàn)運(yùn)算功能,并附帶DateUtils的相關(guān)包文件供讀者下載使用,需要的朋友可以參考下2017-11-11
Spring Security 安全認(rèn)證的示例代碼
這篇文章主要介紹了Spring Security 安全認(rèn)證的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
spring boot創(chuàng)建項(xiàng)目包依賴問(wèn)題的解決
本篇文章主要介紹了spring boot創(chuàng)建項(xiàng)目包依賴問(wèn)題的解決,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11

