最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis下的SQL注入漏洞原理及防護方法解析

 更新時間:2022年11月22日 08:56:25   作者:2ha0yuk7on  
SQL 注入是發(fā)生在 Web 程序中數(shù)據(jù)庫層的安全漏洞,是網(wǎng)站存在最多也是最簡單的漏洞,在實際項目中,即使使用了 Mybatis 框架,但仍然有可能因為編碼人員安全意識不足而導(dǎo)致 SQL 注入問題,這篇文章主要介紹了Mybatis下的SQL注入漏洞原理及防護方法?,需要的朋友可以參考下

之前我一直認(rèn)為 Mybatis 框架下已經(jīng)實現(xiàn)預(yù)編譯機制,很多東西都封裝好了,應(yīng)該基本上不會再有 SQL 注入問題了。近期在滲透中發(fā)現(xiàn),在實際項目中,即使使用了 Mybatis 框架,但仍然有可能因為編碼人員安全意識不足而導(dǎo)致 SQL 注入問題。出現(xiàn)情況還真不少,因此有了這篇文章。

一、前言

之前我一直認(rèn)為 Mybatis 框架下已經(jīng)實現(xiàn)預(yù)編譯機制,很多東西都封裝好了,應(yīng)該基本上不會再有 SQL 注入問題了。近期在滲透中發(fā)現(xiàn),在實際項目中,即使使用了 Mybatis 框架,但仍然有可能因為編碼人員安全意識不足而導(dǎo)致 SQL 注入問題。出現(xiàn)情況還真不少,因此有了這篇文章。

二、SQL 注入漏洞原理

1、概述

SQL 注入(SQL Injection)是發(fā)生在 Web 程序中數(shù)據(jù)庫層的安全漏洞,是網(wǎng)站存在最多也是最簡單的漏洞。主要原因是程序?qū)τ脩糨斎霐?shù)據(jù)的合法性沒有判斷和處理,導(dǎo)致攻擊者可以在 Web 應(yīng)用程序中事先定義好的 SQL 語句中添加額外的 SQL 語句,在管理員不知情的情況下實現(xiàn)非法操作,以此來實現(xiàn)欺騙數(shù)據(jù)庫服務(wù)器執(zhí)行非授權(quán)的任意查詢,從而進(jìn)一步獲取到數(shù)據(jù)信息。

簡單地說,就是通過在用戶可控參數(shù)中注入 SQL 語法,破壞原有 SQL 結(jié)構(gòu),達(dá)到編寫程序時意料之外結(jié)果的攻擊行為。其成因可以歸結(jié)為如下原因造成的:

  • 程序編寫者在處理應(yīng)用程序和數(shù)據(jù)庫交互時,使用字符串拼接的方式構(gòu)造 SQL 語句。
  • 且未對用戶可控參數(shù)進(jìn)行足夠的過濾。

2、漏洞復(fù)現(xiàn)

下面使用DVWA靶場來進(jìn)行演示,網(wǎng)站架構(gòu)為PHP,我們重點關(guān)注漏洞原理即可。

該頁面提供了一個簡單的查詢功能,可以根據(jù)前端輸入的用戶ID來查詢對應(yīng)的用戶信息。如圖,輸入 1,返回了對應(yīng) admin 用戶的信息。

查看該頁面的源代碼:

<?php

if( isset( $_REQUEST[ 'Submit' ] ) ) {
    // Get input
    $id = $_REQUEST[ 'id' ];

    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    // Get results
    while( $row = mysqli_fetch_assoc( $result ) ) {
        // Get values
        $first = $row["first_name"];
        $last  = $row["last_name"];

        // Feedback for end user
        echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
    }

    mysqli_close($GLOBALS["___mysqli_ston"]);
}

?>

進(jìn)行代碼審計可以發(fā)現(xiàn),程序?qū)⑶岸溯斎氲?id 參數(shù)未加任何處理,直接拼接在了 SQL 語句中,那么此時就導(dǎo)致了SQL注入漏洞。

若此時攻擊者輸入的用戶ID為 1' or 1='1,則程序拼接后執(zhí)行的 SQL 語句變成了:

SELECT first_name, last_name FROM users WHERE user_id = '1' or 1='1';

可見,攻擊者通過單引號 ' 閉合了數(shù)據(jù)庫查詢語句,并且在查詢條件之后構(gòu)造了“或 1=1”,即“或真”的邏輯,導(dǎo)致查詢出了全部用戶的數(shù)據(jù)。

如果攻擊者可以任意替代提交的字符串,就可以利用 SQL 注入漏洞改變原有 SQL 語句的含義,進(jìn)而執(zhí)行任意 SQL 命令,入侵?jǐn)?shù)據(jù)庫進(jìn)行脫庫、刪庫,甚至通過數(shù)據(jù)庫提權(quán)獲取系統(tǒng)權(quán)限,造成不可估量的損失。(SQL注入的場景類型非常之多,攻擊手法、繞過姿勢也非常多,本文不作重點討論)

3、修復(fù)建議

一般來說,防御 SQL 注入的最佳方式就是使用預(yù)編譯語句(其他防御方法還有很多,本文不作重點討論),綁定變量。例如:

String sql = "SELECT * FROM user_table WHERE username = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, "zxd");
ResultSet results = pstmt.executeQuery();

使用預(yù)編譯的 SQL 語句,SQL 語句的語義不會發(fā)生改變。在 SQL 語句中,變量用占位符 ? 表示,攻擊者無法改變 SQL 的結(jié)構(gòu)。

三、Mybatis 框架簡介

1、參數(shù)符號的兩種方式

Mybatis 的 SQL 語句可以基于注解的方式寫在類方法上面,更多的是以 xml 的方式寫到 xml 文件。Mybatis 中 SQL 語句需要我們自己手動編寫或者用 generator 自動生成。編寫 xml 文件時,MyBatis 支持兩種參數(shù)符號,#{}${} 。

  • #{} 使用預(yù)編譯,通過 PreparedStatement 和占位符來實現(xiàn),會把參數(shù)部分用一個占位符 ? 替代,而后注入的參數(shù)將不會再進(jìn)行 SQL 編譯,而是當(dāng)作字符串處理。可以有效避免 SQL 注入漏洞。
  • ${} 表示使用拼接字符串,將接受到參數(shù)的內(nèi)容不加任何修飾符拼接在 SQL 中。易導(dǎo)致 SQL 注入漏洞。

兩者的區(qū)別如下:

  • #{} 為參數(shù)占位符 ?,即 SQL 預(yù)編譯。${} 為字符串替換,即 SQL 拼接。
  • #{} 是“動態(tài)解析->預(yù)編譯->執(zhí)行”的過程。${} 是“動態(tài)解析->編譯->執(zhí)行”的過程。
  • #{} 的變量替換是在 DBMS 中。${} 的變量替換是在 DBMS 外。
  • 變量替換后,#{} 對應(yīng)的變量自動加上引號。變量替換后,${} 對應(yīng)的變量不會加上引號。

2、漏洞復(fù)現(xiàn)

下面以一個查詢場景進(jìn)行簡單演示,數(shù)據(jù)庫表 user_table 的表數(shù)據(jù)如下:

若沒有采用 JDBC 的預(yù)編譯模式,查詢 SQL 寫為:

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
    select * from user_table where username = '${username}'
</select>

這種寫法就產(chǎn)生了 SQL 語句的動態(tài)拼接,這樣格式的參數(shù)會直接參與 SQL 語句的編譯,從而不能避免SQL注入攻擊。

若此時攻擊者提交的參數(shù)值為 zxd' or 1='1,如下圖,利用 SQL 注入漏洞,成功查詢了所有用戶數(shù)據(jù)。

因此,應(yīng)用 Mybatis 框架 SQL語句的安全寫法(即 JDBC 預(yù)編譯模式):

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">    select * from user_table where username = #{username}</select>

可見,此時采用 JDBC 預(yù)編譯模式,即使攻擊者嘗試 SQL 注入攻擊,也只會將參數(shù)整體作為字符串處理,有效避免了 SQL 注入問題。

四、Mybatis 框架下的 SQL 注入問題及防護方法

還是以上節(jié)的查詢場景舉例,Mybatis 框架下易產(chǎn)生 SQL 注入漏洞的情況主要有以下三種:

1、模糊查詢

在模糊查詢場景下,考慮安全編碼規(guī)范,使用 #{} 傳入?yún)?shù):

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
    select * from user_table where username = #{username}
</select>

在這種情況下使用 #{} 程序會報錯:

于是很多安全經(jīng)驗不足的程序員就把 #{} 號改成了 ${},如果應(yīng)用層代碼沒有對用戶輸入的內(nèi)容做處理勢必會產(chǎn)生SQL注入漏洞。

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
	select * from user_table where username like '%${username}%'
</select>

若此時攻擊者提交的參數(shù)值為 zxd' or 1=1#,如下圖,利用 SQL 注入漏洞,成功查詢了所有用戶數(shù)據(jù)。

因此,安全的寫法應(yīng)當(dāng)使用 CONCAT 函數(shù)連接通配符:

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
	select * from user_table where username like concat('%',#{username},'%')
</select>

2、帶有 IN 謂詞的查詢

在 IN 關(guān)鍵字之后使用 #{} 查詢多個參數(shù):

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
	select * from user_table where username in (#{usernames})
</select>

正常提交查詢參數(shù) 'zxd','hhh',因為預(yù)編譯機制,系統(tǒng)將我們輸入的字符當(dāng)作了一個字符串,因此查詢結(jié)果為空,不能滿足業(yè)務(wù)功能需求。

于是很多安全經(jīng)驗不足的程序員就把 #{} 號改成了 ${}

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
	select * from user_table where username in (${usernames})
</select>

攻擊者提交參數(shù)值 'hhh') or 1=1#,利用 SQL 注入漏洞,成功查詢了所有用戶數(shù)據(jù)。

因此,此種情況下,安全的做法應(yīng)當(dāng)使用 foreach 標(biāo)簽:

<select id="getUserFromList" resultType="user.NewUserDO">
	select * from user_table where username in
		<foreach collection="list" item="username" open="(" separator="," close=")">
			#{username}
		</foreach>
</select>

3、帶有動態(tài)排序功能的查詢

動態(tài)排序功能,需要在 ORDER BY 之后傳入?yún)?shù),考慮安全編碼規(guī)范,使用 #{} 傳入?yún)?shù):

<select id="getUserOrder" parameterType="java.lang.String" resultType="user.NewUserDO">
	select * from user_table order by #{column} limit 0,1
</select>

提交參數(shù) username 根據(jù)用戶名字段排序。但因為預(yù)編譯機制,系統(tǒng)將我們輸入的字符當(dāng)作了一個字符串,根據(jù)字符串排序是不生效的,不能滿足業(yè)務(wù)功能需求。(根據(jù)用戶名字段排序,此時正常應(yīng)返回 root 用戶)

于是很多安全經(jīng)驗不足的程序員就把 #{} 號改成了 ${}

<select id="getUserOrder" parameterType="java.lang.String" resultType="user.NewUserDO">
	select * from user_table order by ${column} limit 0,1
</select>

攻擊者提交參數(shù)值 username#,利用 SQL 注入漏洞,成功查詢了所有用戶數(shù)據(jù)。

因此,此種情況下,安全的做法應(yīng)當(dāng)在 Java 代碼層面來進(jìn)行解決。可以設(shè)置一個字段值的白名單,僅允許用戶傳入白名單內(nèi)的字段。

String sort = request.getParameter("sort");
String[] sortWhiteList = {"id", "username", "password"};
if(!Arrays.asList(sortWhiteList).contains(sort)){
    sort = "id";
} 

或者僅允許用戶傳入索引值,代碼再將索引值映射成對應(yīng)字段。

String sort = request.getParameter("sort");
switch(sort){
    case "1":
        sort = "id";
        break;
    case "2":
        sort = "username";
        break;
    case "3":
        sort = "password";
        break;
    default:
        sort = "id";
        break;
} 

需要注意的是在 mybatis-generator 自動生成的 SQL 語句中,ORDER BY 使用的也是 ${},而 LIKE 和 IN 沒有問題。

到此這篇關(guān)于Mybatis下的SQL注入漏洞原理及防護方法的文章就介紹到這了,更多相關(guān)Mybatis SQL注入漏洞原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實現(xiàn)字符串倒序輸出的四種方法匯總

    Java實現(xiàn)字符串倒序輸出的四種方法匯總

    這篇文章主要介紹了Java實現(xiàn)字符串倒序輸出的四種方法匯總,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Java批量導(dǎo)出word壓縮后的zip文件案例

    Java批量導(dǎo)出word壓縮后的zip文件案例

    這篇文章主要介紹了Java批量導(dǎo)出word壓縮后的zip文件案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • Spring Boot定時任務(wù)的使用方法

    Spring Boot定時任務(wù)的使用方法

    這篇文章為大家詳細(xì)主要介紹了Spring Boot定時任務(wù)的使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • java編譯時出現(xiàn)使用了未經(jīng)檢查或不安全的操作解決方法

    java編譯時出現(xiàn)使用了未經(jīng)檢查或不安全的操作解決方法

    這篇文章主要介紹了java編譯時出現(xiàn)使用了未經(jīng)檢查或不安全的操作的解決方法,需要的朋友可以參考下
    2014-03-03
  • Java 手寫LRU緩存淘汰算法

    Java 手寫LRU緩存淘汰算法

    本文主要講了如何通過哈希鏈表這種數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)LRU算法,提供了三種實現(xiàn)思路,第一種從雙向鏈表開始,借助于HashMap來實現(xiàn)滿足要求的LRUCache
    2021-05-05
  • Eclipse運行android項目報錯Unable to build: the file dx.jar was not loaded from the SDK folder的解決辦法

    Eclipse運行android項目報錯Unable to build: the file dx.jar was not

    今天小編就為大家分享一篇關(guān)于Eclipse運行android項目報錯Unable to build: the file dx.jar was not loaded from the SDK folder的解決辦法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • SpringBoot整合Redis及Redis工具類撰寫實例

    SpringBoot整合Redis及Redis工具類撰寫實例

    這篇文章主要介紹了SpringBoot整合Redis及Redis工具類撰寫實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • java獲取昨天日期字符串的方法

    java獲取昨天日期字符串的方法

    這篇文章主要介紹了java獲取昨天日期字符串的方法,涉及java針對日期與時間的運算與轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下
    2016-08-08
  • Java責(zé)任鏈設(shè)計模式實例分析

    Java責(zé)任鏈設(shè)計模式實例分析

    這篇文章主要介紹了Java責(zé)任鏈設(shè)計模式,結(jié)合實例形式詳細(xì)分析了Java責(zé)任鏈設(shè)計模式的原理與相關(guān)操作技巧,需要的朋友可以參考下
    2019-07-07
  • alibaba?seata服務(wù)端具體實現(xiàn)

    alibaba?seata服務(wù)端具體實現(xiàn)

    seata是來處理分布式服務(wù)之間互相調(diào)用的事務(wù)問題,本文重點給大家介紹alibaba-seata實現(xiàn)方法,文中通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02

最新評論

余江县| 永春县| 河源市| 澳门| 乌兰察布市| 淄博市| 图们市| 宁化县| 铜山县| 云龙县| 安阳市| 镇远县| 虎林市| 元谋县| 彭水| 神池县| 新竹县| 旬阳县| 巴青县| 阿克苏市| 娄底市| 达拉特旗| 中宁县| 右玉县| 洛阳市| 克拉玛依市| 连南| 文化| 木兰县| 安宁市| 福建省| 区。| 融水| 宁河县| 阿尔山市| 县级市| 黑河市| 南川市| 蚌埠市| 清苑县| 教育|