最近在工作室的分享會(huì)上準(zhǔn)備了SQL注入的內(nèi)容洲押,到這里記錄下鸿竖。
首先沧竟,第一個(gè)問(wèn)題:什么是SQL注入?
百度百科的解釋是:所謂SQL注入千贯,就是通過(guò)把SQL命令插入到Web表單提交或輸入域名或頁(yè)面請(qǐng)求的查詢(xún)字符串屯仗,最終達(dá)到欺騙服務(wù)器執(zhí)行惡意的SQL命令。
從字面上來(lái)看就是在web頁(yè)面上提交一些會(huì)涉及到數(shù)據(jù)庫(kù)操作的數(shù)據(jù)時(shí)搔谴,在數(shù)據(jù)中提交一些SQL命令魁袜,嘗試欺騙數(shù)據(jù)庫(kù)系統(tǒng),讓其運(yùn)行插入進(jìn)去的自定義的SQL語(yǔ)句敦第。
舉個(gè)例子來(lái)看:
這里是mybatis中一個(gè)查找接口的SQL實(shí)現(xiàn)
通過(guò)postman訪問(wèn)接口來(lái)間接調(diào)用這個(gè)實(shí)現(xiàn):
可以看到name參數(shù)那里的值為不正常的數(shù)據(jù)峰弹,但是同樣找到了id為6的用戶(hù)信息,這是為什么呢芜果?
這就是SQL注入:通過(guò)在提交的name數(shù)據(jù)里加入SQL語(yǔ)句使得之前的SQL語(yǔ)句:
SELECT ID,USERNAME,PASSWORD,AGE FROM USER WHERE ID=${arg0} AND USERNAME=${arg1}
在實(shí)際運(yùn)行時(shí)成為了:SELECT ID,USERNAME,PASSWORD,AGE FROM USER WHERE ID=6?AND USERNAME=1 OR 1=1
因?yàn)?=1始終為true鞠呈,所以我可在不知道id為6的username信息時(shí)就能獲取到他的賬號(hào)信息。
那么第二個(gè)問(wèn)題:SQL注入的危害右钾?
從上面的例子就可以看出蚁吝,通過(guò)SQL注入,我們可以在沒(méi)有數(shù)據(jù)庫(kù)權(quán)限的情況下舀射,對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作窘茁,比如獲取數(shù)據(jù)庫(kù)數(shù)據(jù),刪除某個(gè)表等等脆烟。
最后一個(gè)問(wèn)題:如何防止SQL注入山林?
這里我們還是以Mybatis為例:
將SQL實(shí)現(xiàn)部分修改為如上圖所示,然后我們?cè)贉y(cè)試一遍:
可以看到邢羔,用同樣的方式調(diào)用接口時(shí)驼抹,就無(wú)法獲取到id為6的賬號(hào)信息了。那么是哪個(gè)部分防止了SQL注入呢拜鹤,對(duì)比兩個(gè)SQL實(shí)現(xiàn)的截圖可以發(fā)現(xiàn)框冀,下面這張圖中的變量是用#符號(hào)開(kāi)頭,那么這個(gè)符號(hào)為啥可以阻止SQL的注入呢敏簿?
因?yàn)?{}會(huì)先將SQL語(yǔ)句預(yù)編譯明也,為傳入的參數(shù)設(shè)置占位符
${}參數(shù)傳進(jìn)來(lái)之后再編譯SQL語(yǔ)句
而SQL注入發(fā)生在編譯SQL語(yǔ)句階段
這里用#符號(hào)做數(shù)據(jù)變量的開(kāi)頭就是聲明了底層要用PreparedStatement,這是jdbc底層的一個(gè)接口,它的實(shí)例都是SQL語(yǔ)句诡右,而這個(gè)接口會(huì)將它的實(shí)例進(jìn)行預(yù)編譯,然后再將數(shù)據(jù)通過(guò)setXXX方法傳入SQL語(yǔ)句轻猖,然后運(yùn)行帆吻。
簡(jiǎn)單來(lái)說(shuō)#{}會(huì)將SQL語(yǔ)句預(yù)編譯,壞人傳入的SQL語(yǔ)句無(wú)法進(jìn)行編譯咙边,就不會(huì)起作用猜煮。