Mybatis-Flex浅析
字数 1312 2025-08-06 08:35:00

Mybatis-Flex 安全机制与SQL注入防护分析

0x00 关于Mybatis-Flex

MyBatis-Flex是一个MyBatis增强框架,具有轻量、高性能与灵活性的特点。它可以轻松连接任何数据库,其内置的QueryWrapper能显著减少SQL编写工作并降低出错概率。

依赖引入方式:

<dependency>  
   <groupId>com.mybatis-flex</groupId>  
   <artifactId>mybatis-flex-spring-boot-starter</artifactId>  
   <version>1.5.6</version>  
</dependency>

0x01 已有的安全措施

1.1 OrderBy排序安全防护

风险背景
OrderBy子句内容通常是动态生成的,预编译无法处理动态SQL语句,这类场景常存在SQL注入风险。

防护实现

@RequestMapping("/getAllUser")
public ApiResponse<List<User>> getAllUser(String sort) {
    QueryWrapper queryWrapper = QueryWrapper.create().select().orderBy(sort);
    List<User> userList = userMapper.selectListByQuery(queryWrapper);
    return new ApiResponse<>(200, "Success", userList);
}

安全机制:

  1. 通过StringQueryOrderBy构建排序字段
  2. 在构造方法中调用SqlUtil.keepOrderBySqlSafely进行安全检查
  3. 白名单控制:只允许字母、数字、下划线、空格、逗号和点号
  4. 非法字符(如/)会抛出IllegalArgumentException

对比SpringData
SpringData限制更严格,只允许数字、字母、'.'、'_'和''字符:

private static final Predicate<String> predicate = 
    Pattern.compile("^[0-9a-zA-Z_\\.\$\$]*$").asPredicate();

1.2 列名column检查

风险背景
GROUP BY子句的动态性使得预编译阶段难以确定具体分组方式,存在SQL注入风险。

防护实现

@RequestMapping("/groupByCareer")
public ApiResponse<List<Map>> groupByCareer() {
    QueryWrapper queryWrapper = QueryWrapper.create()
        .select("career, COUNT(*)")
        .groupBy("CAREER");
    List<Map> result = userMapper.selectRowsByQuery(queryWrapper);
    return new ApiResponse<>(200, "Success", result);
}

安全机制:

  1. 通过QueryColumn构建列名字段
  2. 调用SqlUtil.keepColumnSafely进行安全检查:
    • 检查空白字符(Character.isWhitespace
    • 检查不安全字符(isUnSafeChar方法)

QueryMethods函数
类似COUNT等方法也有相同安全检查机制。

0x02 常见SQL注入场景

2.1 Mybatis原生注解&XML配置

风险点

  • 使用原生MyBatis功能时,${}参数仍存在注入风险
  • 需检查所有使用${}标注的参数

2.2 QueryWrapper注入风险

2.2.1 动态列名注入

安全方式

QueryWrapper queryWrapper = QueryWrapper.create().select(new QueryColumn("columnName"));
  • 通过QueryColumn构建时会自动安全检查

风险方式

@RequestMapping("/getColumn")
public ApiResponse<List<Map>> getTbale(String column) {
    QueryWrapper queryWrapper = QueryWrapper.create().select(column);
    List<Map> result = userMapper.selectRowsByQuery(queryWrapper);
    return new ApiResponse<>(200, "Success", result);
}
  • 直接传入String通过StringQueryColumn构建时无安全检查
  • 可执行1/0等恶意SQL

2.2.2 Where动态条件注入

风险示例

@RequestMapping("/getUserByFirstName")
public ApiResponse<List<User>> getUserByFirstName(String firstName) {
    QueryWrapper queryWrapper = QueryWrapper.create()
        .select()
        .where("FIRST_NAME='"+firstName+"'");
    List<User> result = userMapper.selectRowsByQuery(queryWrapper);
    return new ApiResponse<>(200, "Success", result);
}
  • 直接SQL拼接导致注入

防护方案

QueryWrapper queryWrapper = QueryWrapper.create()
    .select()
    .where("FIRST_NAME=?", firstName);
  • 使用预编译占位符

其他风险点

  • and (...)or (...)等where条件拓展也存在类似风险

2.3 Db + Row 工具类风险

风险示例

@RequestMapping("/getUserByDbRow")
public ApiResponse<List<Row>> getUserByDbRow(String name) {
    String listsql = "select * from user where first_name = '"+name+"'";
    List<Row> result = Db.selectListBySql(listsql);
    return new ApiResponse<>(200, "Success", result);
}
  • 直接SQL拼接导致注入

防护方案

String listsql = "select * from user where first_name = ?";
List<Row> result = Db.selectListBySql(listsql, name);
  • 使用预编译占位符
  • 对于无法预编译的场景(如排序/动态表名列名)应进行限制处理

安全开发建议

  1. 优先使用预编译:所有动态参数都应使用?占位符
  2. 避免直接拼接SQL:特别是where条件、表名、列名等
  3. 使用安全构建方式
    • 排序使用orderBy(QueryOrderBy...)
    • 列名使用QueryColumn
  4. 严格限制动态内容:对于必须动态的内容实施白名单控制
  5. 定期安全审计:检查所有SQL构建点,特别是${}和直接拼接处

通过遵循这些安全实践,可以充分利用Mybatis-Flex的便利性同时避免SQL注入风险。

Mybatis-Flex 安全机制与SQL注入防护分析 0x00 关于Mybatis-Flex MyBatis-Flex是一个MyBatis增强框架,具有轻量、高性能与灵活性的特点。它可以轻松连接任何数据库,其内置的QueryWrapper能显著减少SQL编写工作并降低出错概率。 依赖引入方式: 0x01 已有的安全措施 1.1 OrderBy排序安全防护 风险背景 : OrderBy子句内容通常是动态生成的,预编译无法处理动态SQL语句,这类场景常存在SQL注入风险。 防护实现 : 安全机制: 通过 StringQueryOrderBy 构建排序字段 在构造方法中调用 SqlUtil.keepOrderBySqlSafely 进行安全检查 白名单控制:只允许字母、数字、下划线、空格、逗号和点号 非法字符(如 / )会抛出 IllegalArgumentException 对比SpringData : SpringData限制更严格,只允许数字、字母、'.'、'_ '和''字符: 1.2 列名column检查 风险背景 : GROUP BY子句的动态性使得预编译阶段难以确定具体分组方式,存在SQL注入风险。 防护实现 : 安全机制: 通过 QueryColumn 构建列名字段 调用 SqlUtil.keepColumnSafely 进行安全检查: 检查空白字符( Character.isWhitespace ) 检查不安全字符( isUnSafeChar 方法) QueryMethods函数 : 类似COUNT等方法也有相同安全检查机制。 0x02 常见SQL注入场景 2.1 Mybatis原生注解&XML配置 风险点 : 使用原生MyBatis功能时, ${} 参数仍存在注入风险 需检查所有使用 ${} 标注的参数 2.2 QueryWrapper注入风险 2.2.1 动态列名注入 安全方式 : 通过 QueryColumn 构建时会自动安全检查 风险方式 : 直接传入String通过 StringQueryColumn 构建时无安全检查 可执行 1/0 等恶意SQL 2.2.2 Where动态条件注入 风险示例 : 直接SQL拼接导致注入 防护方案 : 使用预编译占位符 其他风险点 : and (...) 和 or (...) 等where条件拓展也存在类似风险 2.3 Db + Row 工具类风险 风险示例 : 直接SQL拼接导致注入 防护方案 : 使用预编译占位符 对于无法预编译的场景(如排序/动态表名列名)应进行限制处理 安全开发建议 优先使用预编译 :所有动态参数都应使用 ? 占位符 避免直接拼接SQL :特别是where条件、表名、列名等 使用安全构建方式 : 排序使用 orderBy(QueryOrderBy...) 列名使用 QueryColumn 严格限制动态内容 :对于必须动态的内容实施白名单控制 定期安全审计 :检查所有SQL构建点,特别是 ${} 和直接拼接处 通过遵循这些安全实践,可以充分利用Mybatis-Flex的便利性同时避免SQL注入风险。