原创 | tkMybatis中常见的注入场景

admin 2021年11月30日04:09:46评论451 views字数 6529阅读21分45秒阅读模式
原创 | tkMybatis中常见的注入场景
点击上方蓝字 关注我吧

原创 | tkMybatis中常见的注入场景
Mybatis的注解
原创 | tkMybatis中常见的注入场景


Tkmybatis 是基于 Mybatis 框架开发的一个工具,通过调用它提供的方法实现对单表的数据操作,不需要写任何 sql 语句,极大地提高了项目开发效率。

1.1 相关依赖

<dependency><groupId>tk.mybatis</groupId><artifactId>mapper</artifactId><version>版本号</version></dependency>

1.2 使用方法

引入tk.mybatis的依赖后。定义数据库表映射类:

@Datapublic class User {private Long id;private String name;private Integer age;private String email;}

mapepr继承其提供的接口,常见的有:

  • BaseMapper

  • MySqlMapper

  • IdsMapper

  • ConditionMapper

  • ExampleMapper


这里以BaseMapper为例:

import tk.mybatis.mapper.common.BaseMapper;public interface UserMapper extends BaseMapper<User>{}

然后就可以调用接口中封装好的方法进行SQL操作了,例如通过selectAll查询所有的user信息:

@Resourceprivate UserMapper userMapper;
@RequestMapping(value = "/getUserInfo")public List<User> getUserInfo(){ List<User> result =userMapper.selectAll();return result; }

原创 | tkMybatis中常见的注入场景

对应的方法实现原理也很简单,主要是通过mybatis的Provider注解来实现 的:

public interface SelectAllMapper<T> {/**   * 查询全部结果   *   * @return   */@SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL")  List<T> selectAll();}
/**   * 查询全部结果   *   * @param ms   * @return   */  public String selectAll(MappedStatement ms) {    final Class<?> entityClass = getEntityClass(ms);//修改返回值类型为实体类型    setResultType(ms, entityClass);    StringBuilder sql = new StringBuilder();    sql.append(SqlHelper.selectAllColumns(entityClass));    sql.append(SqlHelper.fromTable(entityClass, tableName(entityClass)));// 逻辑删除的未删除查询条件    sql.append("<where>");    sql.append(SqlHelper.whereLogicDelete(entityClass, false));    sql.append("</where>");        sql.append(SqlHelper.orderByDefault(entityClass));return sql.toString();  }

因为Provider其实只需要返回一个 SQL 字符串 ,所以tkMybatis在防护 SQL 注入时,也是对对应的内容,通过#{}的格式来进行预编译的 ,例如其中的一个拼接method:

/**   * 返回格式如:#{entityName.age+suffix,jdbcType=NUMERIC,typeHandler=MyTypeHandler}+separator   *   * @param entityName   * @param suffix   * @param separator   * @return   */public String getColumnHolder(String entityName, String suffix, String separator) {    StringBuffer sb = new StringBuffer("#{");if (StringUtil.isNotEmpty(entityName)) {      sb.append(entityName);      sb.append(".");    }    sb.append(this.property);if (StringUtil.isNotEmpty(suffix)) {      sb.append(suffix);    }//如果 null 被当作值来传递,对于所有可能为空的列,JDBC Type 是需要的if (this.jdbcType != null) {      sb.append(", jdbcType=");      sb.append(this.jdbcType.toString());    }//为了以后定制类型处理方式,你也可以指定一个特殊的类型处理器类,例如枚举if (this.typeHandler != null) {      sb.append(", typeHandler=");      sb.append(this.typeHandler.getCanonicalName());    }//3.4.0 以前的 mybatis 无法获取父类中泛型的 javaType,所以如果使用低版本,就需要设置 useJavaType = true//useJavaType 默认 false,没有 javaType 限制时,对 ByPrimaryKey 方法的参数校验就放宽了,会自动转型if (useJavaType && !this.javaType.isArray()) {//当类型为数组时,不设置javaType#103      sb.append(", javaType=");      sb.append(javaType.getCanonicalName());    }    sb.append("}");if (StringUtil.isNotEmpty(separator)) {      sb.append(separator);    }return sb.toString();  }

同时也支持Myabtis的xml和注解的配置方式。此外还提供了扩展机制,通过 Example 和 Criteria 类进行动态SQL 的拼装。


原创 | tkMybatis中常见的注入场景

常见SQL注入场景

原创 | tkMybatis中常见的注入场景

2.1 Example

通过提供的Example可以添加查询条件,相当于是sql语句中的where后面的条件部分。例如如下的例子:

mapper继承tk.mybatis.mapper.common.Mapper(包含了tk.mybatis.mapper.common.ExampleMapper):

public interface UserMapper extends Mapper<User>{}

在service层,通过Example生成对应的动态sql并封装在方法里:

@Servicepublic class UserServiceImpl implements IUserService{@Autowired        UserMapper userMapper;
@Overridepublic List<User> findByUserName(String username) {// TODO Auto-generated method stub Example example = new Example(User.class); Example.Criteria criteria = example.createCriteria(); criteria.andLike("name", username); List<User> userList = userMapper.selectByExample(example);return userList; }
}

Controller里调用service的方法:

@Autowired    IUserService userService;
@RequestMapping(value = "/getUserInfoByUserName")public List<User> getUserInfoByUserName(String username){ List<User> result =userService.findByUserName(username);return result; }

直接访问接口即可进行模糊查询:

原创 | tkMybatis中常见的注入场景

同时也进行了预编译处理:

原创 | tkMybatis中常见的注入场景

跟所有的SQL注入一样,动态表名&Order by查询没办法进行预编译处理,会存在SQL拼接。Example 和 Criteria也提供了相关的method。

2.1.1 order by排序

可以通过example.setOrderByClause()方法来进行排序 。这里若前端可控且没有进行相关的安全检查会存在SQL注入风险:

@Overridepublic List<User> sortBy(String sort) {// TODO Auto-generated method stub                Example example = new Example(User.class);                example.setOrderByClause(sort);                List<User> userList = userMapper.selectByExample(example);return userList;        }

原创 | tkMybatis中常见的注入场景

尝试写入1/0,成功触发SQL error,存在注入风险:

原创 | tkMybatis中常见的注入场景

对于这种情况,除了对输入参数进行安全检查,还可以通过Example.builder方式进行查询,其会将查询内容与entity进行绑定,在一定程度上规避了注入的风险:

Example example = Example.builder(User.class)                    .orderByDesc(sort)                    .build();

原创 | tkMybatis中常见的注入场景

或者使用example.orderBy()方法,同样将查询内容与entity进行绑定:

private String property(String property) {if (StringUtil.isEmpty(property) || StringUtil.isEmpty(property.trim())) {throw new MapperException("接收的property为空!");      }      property = property.trim();if (!propertyMap.containsKey(property)) {throw new MapperException("当前实体类不包含名为" + property + "的属性!");      }return propertyMap.get(property).getColumn();    }

2.1.2 动态表名

可以通过以下方式实现动态表名。

首先在实体Dao添加非表字段参数,用于接受动态表名参数:

@Transientprivate String tableName;

然后实现接口IDynamicTableName,并实现getDynamicTableName()方法:

@Datapublic class User implements IDynamicTableName{
@Transient        private String tableName;
public Long getId() {return id; }
public String getDynamicTableName() { ......}

然后通过example.setTableName()即可设置查询的动态表名:      

@Autowired        UserMapper userMapper;
@Overridepublic List<User> DynamicTableName(String tableName) {// TODO Auto-generated method stub Example example = new Example(User.class); example.setTableName(tableName); example.setOrderByClause("id desc"); List<User> userList = userMapper.selectByExample(example);return userList; }

例如查询user表的内容:

原创 | tkMybatis中常见的注入场景

  因为这里是动态拼接的,实际上存在SQL注入风险:

原创 | tkMybatis中常见的注入场景

可以看到user表后的内容where 1=1/0成功拼接到SQL中并执行:

原创 | tkMybatis中常见的注入场景

2.2 Condition

Condition条件查询,其继承了Example:

public class Condition extends Example {public Condition(Class<?> entityClass) {super(entityClass);  }
public Condition(Class<?> entityClass, boolean exists) {super(entityClass, exists);  }
public Condition(Class<?> entityClass, boolean exists, boolean notNull) {super(entityClass, exists, notNull); }}

也就说说动态表名跟order by排序的场景也是存在的:

condition.setOrderByClause();condition.setTableName();

2.3 criteria动态SQL拼接

Criteria是Example中的一个内部类,在最终sql构建时以括号呈现,Criteria里带了较多构建查询条件的方法,方便生成动态SQL。

Example example = new Example(CcompareccicModel.class); Criteria criteria = example.createCriteria();

criteria的如下方法也是直接进行SQL拼接的:

criteria.andCondition(String condition)criteria.andCondition(String condition,Object value)criteria.orCondition(String condition)criteria.orCondition(String condition,Object value)

例如andCondition方法实际上是增加了动态查询的条件:

/**     * 手写条件     *     * @param condition 例如 "length(countryname)<5"     * @return     */public Criteria andCondition(String condition) {      addCriterion(condition);return (Criteria) this;    }

/** * 手写左边条件,右边用value值 * * @param condition 例如 "length(countryname)=" * @param value 例如 5 * @return */public Criteria andCondition(String condition, Object value) { criteria.add(new Criterion(condition, value));return (Criteria) this; }

2.4 注解&xml配置

因为tkmybatis同时也支持Myabtis的xml和注解的配置方式,所以跟mybatis一样,关注${}格式的动态SQL即可。


原创 | tkMybatis中常见的注入场景

参考资料

原创 | tkMybatis中常见的注入场景


https://github.com/abel533/Mapper


相关推荐




原创 | Digvuln Tricks之JS泄露全到后台越权
原创 |【胖哈勃的七月公开赛】NewSql
原创 | 记一次完成的钓鱼实战
原创 | AMSI 浅析及绕过

原创 | tkMybatis中常见的注入场景
你要的分享、在看与点赞都在这儿~

本文始发于微信公众号(SecIN技术平台):原创 | tkMybatis中常见的注入场景

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年11月30日04:09:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   原创 | tkMybatis中常见的注入场景https://cn-sec.com/archives/456983.html

发表评论

匿名网友 填写信息