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

Mybatis-plus?sql注入及防止sql注入詳解

 更新時間:2022年10月27日 09:51:34   作者:sunrj_go  
mybatis-plus提供了許多默認單表 CRUD 語句,對于其他SQL情況愛莫能助,下面這篇文章主要給大家介紹了關于Mybatis-plus?sql注入及防止sql注入的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下

一、SQL注入是什么?

SQL注入是一種代碼注入技術(shù),用于攻擊數(shù)據(jù)驅(qū)動的應用,惡意的SQL語句被 插入到執(zhí)行的SQL語句中來改變查詢結(jié)果,例如: OR 1=1 或者 ;drop table sys_user;等等

二、mybatis是如何做到防止sql注入的

mybatis中我們所寫的sql語句都是在xml只能完成,我們在編寫sql會用到 #{},${} 這個兩個表達式。那 #{} 和 ${}兩者之間有什么區(qū)別嘞?下面我將用兩個SQL語句例子來進行說明。

<select id="selectUserByUserName" parameterType="java.lang.String" resultType="com.domain.UserInfo">
	SELECT USER_ID, USER_NAME, PWD, USER_PHONE FROM SYS_USER 
	<where>
		USER_ID= #{userName,jdbcType=VARCHAR} 
	</where> 
</select>
<select id="selectUserByUserName" parameterType="java.lang.String" resultType="com.domain.UserInfo">
	SELECT USER_ID, USER_NAME, PWD, USER_PHONE FROM SYS_USER 
	<where>
		USER_NAME= ${userName,jdbcType=VARCHAR} 
	</where> 
</select>
  • 第一種SQL語句中使用的#{}方式,#{}中當傳入的數(shù)據(jù)是字符串,會在使用" "雙引號將值引起來。
  • 示例:例如 userName 傳入的值是 9;DROP TABLE SYS_USER;那么#{}去取后得到的結(jié)果就是 USER_NAME="9;DROP TABLE SYS_USER;"就算傳入刪除表的命令也不會被執(zhí)行,因為9;DROP TABLE SYS_USER;會幫當成一個完成的字符串去進行值匹配。
  • 第二種SQL${}方式取值,那就變成了USER_NAME=9;DROP TABLE SYS_USER; , 因 為 ${}直接將值拼接在SQL語句后面的,使其成為SQL,因此直接將值拼接在SQL語句后面的,因此${}是存在SQL注入的風險的,在使用時要注意手動處理。

1. #{} 和 ${} 兩者的區(qū)別

  • #{}:解析為一個 JDBC 預編譯語句,一個 #{} 被解析為一個參數(shù)占位符 ? ,#{}方式將傳入的數(shù)據(jù)都當成一個字符串,會對自動傳入的數(shù)據(jù)加一個雙引號。 如:WHERE USER_NAME =#{username},如果傳入的值是9,那么解析成sql時的值為WHERE USER_NAME =“9”,如果傳入的值是12345678,則解析成的sql為WHERE USER_NAME =“12345678”,
  • ${} 僅 僅 為 一 個 純 粹 的 s t r i n g 替 換 ,${}方式傳入的變量直接拼接在sql中。如:WHERE USER_NAME = ${username},如果傳入的值是9,那么解析成sql時的值為WHERE USER_NAME =9; 如果傳入的值是;DROP TABLE SYS_USER;,則解析成的sql為:SELECT USER_ID, USER_NAME, PWD, USER_PHONE FROM SYS_USER WHERE USER_NAME="9;DROP TABLE SYS_USER;所以象 ORDER BY 或者 GROUP BY 等可以使用 ${}方式。
  • #{}方式底層采用預編譯方式PreparedStatement,能夠很大程度防止sql注入,因為SQL注入發(fā)生在編譯時;${}方式底層只是Statement,無法防止Sql注入。
    $方式一般用于傳入數(shù)據(jù)庫對象,例如傳入表名

2.PreparedStatement和Statement的區(qū)別

① PreparedStatement 在執(zhí)行sql命令時,命令會先被數(shù)據(jù)庫進行解析和編譯,然后將其放到命令緩存區(qū),然后,當每一個執(zhí)行的相同的sql 命令時,若在緩存區(qū)發(fā)了編譯命令,就不會再次進行解析和編譯,這樣就可以進行重復使用。PreparedStatement 在編譯是會將每個#{}標記符號解析為參數(shù)參數(shù)占位符?,傳入的變量就是做為參數(shù),不會對sql語句進行修改,這樣就能防止SQL注入的攻擊。‘’

②Statement是直接將Sql命令直接交給數(shù)據(jù)庫進行運行,不能做到攔截SQL注入的攻擊,因為SQL注入時發(fā)生在運行時。Statement每次都會對SQL命令進行解析和編譯,增加大數(shù)據(jù)庫的開銷,因此它效率不如PreparedStatement。

3.什么是預編譯

預編譯是做些代碼文本的替換工作。是整個編譯過程的最先做的工作。處理以# 開頭的指令 , 比如拷貝 #include 包含的文件代碼,#define 宏定義的替換 , 條件編譯等,就是為編譯做的預備工作的階段。主要處理#開始的預編譯指令,預編譯指令指示了在程序正式編譯前就由編譯器進行的操作,可以放在程序中的任何位置。而SQL注入只能發(fā)生在運行時。

4.mybaits-plus sql注入產(chǎn)生的原因

Mybatisplus中的 PaginationInterceptor 主要用于處理數(shù)據(jù)庫的物理分頁,避免內(nèi)存分頁。
分析PaginationInterceptor 的源碼可以發(fā)現(xiàn)

Orderby場景下的SQL注入

前面提到了分頁中會存在Orderby的使用,因為Orderby動態(tài)查詢沒辦法進行預編譯,所以不經(jīng)過安全檢查的話會存在注入風險。PaginationInnerInterceptor主要是通過設置com.baomidou.mybatisplus.extension.plugins.pagination.page對象里的屬性來實現(xiàn)orderby的,主要是以下函數(shù)的調(diào)用,因為直接使用sql拼接,所以需要對進行排序的列名進行安全檢查:

page.setAscs();
page.setDescs();

源碼:

可以看出,分頁是通過字符串拼接的方式,所以出現(xiàn)SQL注入的風險

 public static String concatOrderBy(String originalSql, IPage<?> page, boolean orderBy) {
        if (!orderBy || !ArrayUtils.isNotEmpty(page.ascs()) && !ArrayUtils.isNotEmpty(page.descs())) {
            return originalSql;
        } else {
            StringBuilder buildSql = new StringBuilder(originalSql);
            String ascStr = concatOrderBuilder(page.ascs(), " ASC");
            String descStr = concatOrderBuilder(page.descs(), " DESC");
            if (StringUtils.isNotEmpty(ascStr) && StringUtils.isNotEmpty(descStr)) {
                ascStr = ascStr + ", ";
            }

            if (StringUtils.isNotEmpty(ascStr) || StringUtils.isNotEmpty(descStr)) {
                buildSql.append(" ORDER BY ").append(ascStr).append(descStr);
            }

            return buildSql.toString();
        }
    }

三、Mybatis-plus是如何做到防止sql注入的

在使用分頁的controller,對傳入的分頁插件,對ascs與descs進行檢查,判斷是否有非法字符,如有,則提示參數(shù)中含有非法的列名:create_time aaaa

示例:

校驗字段的util:

package com.koal.util;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.koal.exception.BizException;
import com.koal.web.ErrorCode;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.Optional;
import java.util.regex.Pattern;

/**
 * @author sunrj
 */
public class RegexUtils {

    /**
     * 對Page校驗防止sql注入
     *
     * @param
     */
    public static void verifyPageFileld(Page page) {

        //asc校驗
        Optional.ofNullable(page.ascs()).ifPresent(ascs ->  {
            Arrays.asList(ascs).forEach(asc -> {
                boolean rightfulString = RegexUtils.isRightfulString(asc);
                if (!rightfulString) {
                    throw new BizException(ErrorCode.COMMON_VERIFY_ERROR.getCode(), "ascs參數(shù)中含有非法的列名:" + asc);
                }
            });
        });
        //desc校驗
        Optional.ofNullable(page.descs()).ifPresent(descs ->  {
            Arrays.asList(descs).forEach(desc -> {
                boolean rightfulString = RegexUtils.isRightfulString(desc);
                if (!rightfulString) {
                    throw new BizException("10011", "desc參數(shù)中含有非法的列名:" + desc);
                }
            });
        });
    }

    /**
     * 判斷是否為合法字符(a-zA-Z0-9-_)
     *
     * @param text
     * @return
     */
    public static boolean isRightfulString(String text) {
        return match(text, "^[A-Za-z0-9_-]+$");
    }

    /**
     * 正則表達式匹配
     *
     * @param text 待匹配的文本
     * @param reg  正則表達式
     * @return
     */
    private static boolean match(String text, String reg) {
        if (StringUtils.isBlank(text) || StringUtils.isBlank(reg)) {
            return false;
        }
        return Pattern.compile(reg).matcher(text).matches();
    }
}

controller校驗page中的字段:

@GetMapping
	@ApiOperation(value = "查詢用戶列表", notes = "查詢用戶列表")
	public ServerResponse<IPage<Account>> queryAccount(Page<Account> page) {
	    //校驗page中的字段,防止sql注入
		RegexUtils.verifyPageFileld(page);
		return ServerResponse.successMethod(accountService.query(page));
	}

結(jié)果:

POST http://127.0.0.1:8080/account?current=1&size=10&ascs=create_time;DROP TABLE tb_account;


結(jié)果:
{
    "code": "10011",
    "msg": "ascs參數(shù)中含有非法的列名:create_time;DROP TABLE ag_account_info;",
    "timestamp": 1653547051505
}

補充:Mybatis Plus自定義全局SQL注入

實現(xiàn)步驟如下:

  • 在 Mapper接口中定義相關的 CRUD方法
  • 擴展 AutoSqlInjector inject 方法,實現(xiàn) Mapper接口中方法要注入的 SQL
  • 在 MP全局策略中,配置 自定義注入器

① mapper中定義業(yè)務方法

如下所示:

public interface EmployeeMapper extends BaseMapper<Employee> {
    int  deleteAll();
}

② 實現(xiàn)自己的MySqlInjector

如下所示:

/**
 * 自定義全局操作
 */
public class MySqlInjector  extends AutoSqlInjector{
    /**
     * 擴展inject 方法,完成自定義全局操作
     */
    @Override
    public void inject(Configuration configuration, MapperBuilderAssistant builderAssistant, Class<?> mapperClass,
            Class<?> modelClass, TableInfo table) {
        //將EmployeeMapper中定義的deleteAll, 處理成對應的MappedStatement對象,加入到configuration對象中。
        
        //注入的SQL語句
        String sql = "delete from " +table.getTableName();
        //注入的方法名   一定要與EmployeeMapper接口中的方法名一致
        String method = "deleteAll" ;
        
        //構(gòu)造SqlSource對象
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        
        //構(gòu)造一個刪除的MappedStatement
        this.addDeleteMappedStatement(mapperClass, method, sqlSource);
    }
}

③ 把自定義的MySqlInjector 配置到全局策略中

如果是xml配置方式,實例如下:

<!-- 定義MybatisPlus的全局策略配置-->
<bean id ="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<!-- 在2.3版本以后,dbColumnUnderline 默認值就是true -->
<property name="dbColumnUnderline" value="true"></property>

<!-- Mysql 全局的主鍵策略 -->
<property name="idType" value="0"></property>?

<!-- 全局的表前綴策略配置 -->
<property name="tablePrefix" value="tbl_"></property>

<!--注入自定義全局操作 ?? ?-->
<property name="sqlInjector" ref="mySqlInjector"></property>
</bean>

<!-- 定義自定義注入器 -->
<bean id="mySqlInjector" class="com.jane.mp.injector.MySqlInjector"></bean>

總結(jié)

到此這篇關于Mybatis-plus sql注入及防止sql注入的文章就介紹到這了,更多相關Mybatis-plus防止sql注入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論

湘西| 桐乡市| 巴林左旗| 阿拉善盟| 四平市| 舒城县| 娄烦县| 阳谷县| 昌邑市| 沙湾县| 镇平县| 香港| 长兴县| 长沙市| 台中市| 吴江市| 梓潼县| 阿克陶县| 东宁县| 弋阳县| 合阳县| 昌黎县| 济南市| 隆昌县| 阿坝| 津南区| 成都市| 舒兰市| 海原县| 青川县| 三江| 七台河市| 永川市| 许昌县| 搜索| 湘潭市| 资源县| 忻州市| 普宁市| 河西区| 新巴尔虎右旗|