文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

Mybatis的SQL注入实例分析

2023-06-29 04:49

关注

本文小编为大家详细介绍“Mybatis的SQL注入实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“Mybatis的SQL注入实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

前言

MyBatis3提供了新的基于注解的配置。主要在MapperAnnotationBuilder中,定义了相关的注解:

public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {    ...    sqlAnnotationTypes.add(Select.class);    sqlAnnotationTypes.add(Insert.class);    sqlAnnotationTypes.add(Update.class);    sqlAnnotationTypes.add(Delete.class);    ......    sqlProviderAnnotationTypes.add(SelectProvider.class);    sqlProviderAnnotationTypes.add(InsertProvider.class);    sqlProviderAnnotationTypes.add(UpdateProvider.class);    sqlProviderAnnotationTypes.add(DeleteProvider.class);}

增删改查占据了绝大部分的业务操作,通过注解不在需要配置繁杂的xml文件,越来越多的sql交互均通过注解来实现。从MapperAnnotationBuilder可以看到Mybatis提供了以下相关的注解:

例如如下例子,使用@Select注解直接编写SQL完成数据查询:

@Mapperpublic interface UserMapper {    @Select("select * from t_user")    List<User> list();}

使用类似@SelectProvider高级注解可以指定某个工具类的方法来动态编写SQL,以应对复杂的业务需求。
以@SelectProvider 为例,查看具体的实现,主要包含两个注解属性,其中type表示工具类,method 表示工具类的某个方法,用于返回具体的SQL:

@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface InsertProvider {    // 用于指定获取 sql 语句的指定类    Class<?> type();    // 指定类中要执行获取 sql 语句的方法    String method();}

使用方法如下,在ProjectSql类的getContentByProjectIds方法定义相关的sql即可,sql的定义可以通过org.apache.ibatis.jdbc.SQL来快速实现:
@SelectProvider(type = ProjectSql.class, method = "getContentByProjectIds")
List<Integer> getContentByProjectIds(List<Integer> projectIds);

常见注入场景

2.1普通注解

实际上跟xml配置中对应的标签语法是一样的(例如@Select对应<select>标签),所以注入场景也是类似的。

在Mybatis中,#的作用主要是替换预编译语句(PrepareStatement)中的占位符?,$是直接的SQL拼接。以like模糊查询 为例子:

例如如下例子:

跟xml配置一样,like模糊查询直接使用#预编译的方式进行注解的话会触发异常,所以很多时候直接使用$进行注解:

@Select("SELECT id, name, age, email FROM user where name like '${name}'")List<User> queryUserByName(@Param("name") String name);

那么此时name前端用户可控的话,将导致SQL注入风险。

图片查看sql日志,成功执行1/0触发sql error,说明注入成功:

处理这类SQL问题也很简单,使用sql的内置函数进行拼接,拼接后再采用#预编译的方式进行查询。例如上面案例是h3数据库的,使用'||'拼接再进行预编译处理即可:

@Select("SELECT id, name, age, email FROM user where name like '%'||#{name}||'%'")List<User> queryUserByName(@Param("name") String name);

此时已使用预编译进行SQL查询:

此外,类似Order by、动态表名,无法采用预编译的方式情况,可以在在代码层使用间接引用的方式进行处理。

对于范围查询in,熟悉mybatis注入的话,是需要使用MyBatis自带的循环指令foreach来解决SQL语句动态拼接的,当使用注解时,就需要使用< script>标签来引入foreach了。

2.2 动态sql

2.2.1 使用< script>

要在带普通注解的映射器接口类中使用动态 SQL,可以使用script 元素。跟xml类似,主要是如下的元素:

ifchoose (when, otherwise)trim (where, set)foreach

相关的注入场景跟2.1也是类似的。也是离不开$。此外,在进行同条件多值查询(例如范围查询in)的时候,可以使用MyBatis自带的循环指令foreach来解决SQL语句动态拼接的问题。

2.2.2 使用Provider注解

可以通过使用Provider注解指定某个工具类的方法来动态编写SQL。以@SelectProvider为例:

首先在mapper中使用@SelectProvider定义相关的方法,其中type表示工具类,method 表示工具类的某个方法,用于返回具体的SQL。例如下面的例子:
通过传递userIds以及name,查询相关的用户信息,在UserInfoSql类的getUserInfoByids方法定义了具体的SQL内容:

  @SelectProvider(type = UserInfoSql.class, method = "getUserInfoByids")  List<User> getUserInfoByids(List<Long> userIds, String name);     class UserInfoSql {    public String getUserInfoByids(List<Long> userIds, String name) {      SQL sql = new SQL();      sql.SELECT("id, name, age, email");      sql.FROM("user");      sql.WHERE("id in(" + Joiner.on(',').join(userIds) + ")");      if(StringUtil.isNotBlank(name)){        sql.WHERE("name like '%" + name + "%'");      }      sql.ORDER_BY("id desc");      return sql.toString();    }  }

在Controller调用具体方法就可以进行sql查询了:

@RequestMapping(value = "/getUserInfoByids") public List<User> getUserInfoByids(String name,@RequestParam List<Long> userIds){         List<User> userList = userMapper.getUserInfoByids(userIds,name);         return userList; }

正常请求返回对应的用户信息:

前面是通过MyBatis 3 提供的工具类org.apache.ibatis.jdbc.SQL来生成SQL的。该类提供了类似select、where、ORDER_BY等方法来完成SQL生成的操作。这里有个误区,很多开发认为这里工具类会进行相关的预编译处理。

实际上Provider其实只需要返回一个SQL字符串,工具类只不过用了一些关键字做格式化而已,甚至可以直接使用StringBuffer拼接SQL语句。同样是上面的例子,List userIds是long类型,但是name是String类型,可以尝试注入:

查看相关日志,成功执行1/0逻辑触发SQL error,也印证了Provider实际上只是 SQL 拼接,没有做相关的安全处理 :

相比@Select@,SelectProvider 只是在定义注解的方式上有所不同, 前者是直接定义 sql, 一个是在外部定义好 sql 直接引用, 没本质上的区别,所以解决方法是在对应的sql场景,使用#进行预编译进行处理,例如这里的like模糊查询和in范围查询:

@SelectProvider(type = UserInfoSql.class, method = "getUserInfoByids")  List<User> getUserInfoByids(@Param("userIds")List<Long> userIds,@Param("name")String name);     class UserInfoSql {    public String getUserInfoByids(@Param("userIds")List<Long> userIds, @Param("name")String name) {            StringBuilder sql = new StringBuilder(128);            sql.append("< script>SELECT id, name, age, email FROM user WHERE (id in");            sql.append("<foreach item='item' collection='userIds' open='(' separator=',' close=')'>#{item}</foreach>");      if(StringUtil.isNotBlank(name)){              sql.append("and name like '%'||#{name}||'%')");      }      sql.append("ORDER BY id desc</script>");      return sql.toString();    }  }

查看sql日志,此时使用预编译进行sql处理,避免了SQL注入风险。

读到这里,这篇“Mybatis的SQL注入实例分析”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

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

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

软考中级精品资料免费领

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

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

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

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

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

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

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