文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Mybatis-plus sql注入及防止sql注入详解

2022-11-13 18:57

关注

一、SQL注入是什么?

SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被 插入到执行的SQL语句中来改变查询结果,例如: OR 1=1 或者 ;drop table sys_user;等等

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

mybatis中我们所写的sql语句都是在xml只能完成,我们在编写sql会用到 #{},${} 这个两个表达式。那 #{} 和 ${}两者之间有什么区别嘞?下面我将用两个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>

1. #{} 和 ${} 两者的区别

2.PreparedStatement和Statement的区别

① PreparedStatement 在执行sql命令时,命令会先被数据库进行解析和编译,然后将其放到命令缓存区,然后,当每一个执行的相同的sql 命令时,若在缓存区发了编译命令,就不会再次进行解析和编译,这样就可以进行重复使用。PreparedStatement 在编译是会将每个#{}标记符号解析为参数参数占位符?,传入的变量就是做为参数,不会对sql语句进行修改,这样就能防止SQL注入的攻击。‘’

②Statement是直接将Sql命令直接交给数据库进行运行,不能做到拦截SQL注入的攻击,因为SQL注入时发生在运行时。Statement每次都会对SQL命令进行解析和编译,增加大数据库的开销,因此它效率不如PreparedStatement。

3.什么是预编译

预编译是做些代码文本的替换工作。是整个编译过程的最先做的工作。处理以# 开头的指令 , 比如拷贝 #include 包含的文件代码,#define 宏定义的替换 , 条件编译等,就是为编译做的预备工作的阶段。主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。而SQL注入只能发生在运行时。

4.mybaits-plus sql注入产生的原因

Mybatisplus中的 PaginationInterceptor 主要用于处理数据库的物理分页,避免内存分页。
分析PaginationInterceptor 的源码可以发现

Orderby场景下的SQL注入

前面提到了分页中会存在Orderby的使用,因为Orderby动态查询没办法进行预编译,所以不经过安全检查的话会存在注入风险。PaginationInnerInterceptor主要是通过设置com.baomidou.mybatisplus.extension.plugins.pagination.page对象里的属性来实现orderby的,主要是以下函数的调用,因为直接使用sql拼接,所以需要对进行排序的列名进行安全检查:

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

源码:

可以看出,分页是通过字符串拼接的方式,所以出现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进行检查,判断是否有非法字符,如有,则提示参数中含有非法的列名: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;


public class RegexUtils {

    
    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参数中含有非法的列名:" + 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参数中含有非法的列名:" + desc);
                }
            });
        });
    }

    
    public static boolean isRightfulString(String text) {
        return match(text, "^[A-Za-z0-9_-]+$");
    }

    
    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));
	}

结果:

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


结果:
{
    "code": "10011",
    "msg": "ascs参数中含有非法的列名:create_time;DROP TABLE ag_account_info;",
    "timestamp": 1653547051505
}

补充:Mybatis Plus自定义全局SQL注入

实现步骤如下:

① mapper中定义业务方法

如下所示:

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

② 实现自己的MySqlInjector

如下所示:


public class MySqlInjector  extends AutoSqlInjector{
    
    @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" ;
        
        //构造SqlSource对象
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        
        //构造一个删除的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>

总结

到此这篇关于Mybatis-plus sql注入及防止sql注入的文章就介绍到这了,更多相关Mybatis-plus防止sql注入内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯