结论
先说结论,不反射或修改生成的代码,且排序从句不依赖前端数据,就可以防御
验证
直接进入逆向工程生成的代码中检查
那自然是从 MyBatis Generator 生成的 XML 映射文件看起,众所周知,MyBatis 中参数占位符 “#{}” 是可以抵御 SQL 注入的,而字符串拼接 “${}” 则会引入风险;考虑以下生成的 XML 片段,重点关注条件语句和排序从句的构造方式:
<select id="selectByExample" parameterType="com.macro.mall.model.PmsPriceDetailExample" resultMap="BaseResultMap">
select
...
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause} <!-- 可能的注入点 -->
</if>
</select>
<sql id="Example_Where_Clause">
<where>
...
<if test="criteria.valid">
<when test="criterion.noValue">
and ${criterion.condition} <!-- 可能的注入点 -->
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value} <!-- 后面的值填入安全使用了参数占位符 -->
</when>
</if>
...
</where>
</sql>
可以看到,两种占位方式都有使用,毕竟字符串拼接才能动态满足条件、排序设置的要求,但这也并不意味着使用 “${}” 的地方就都是危险的,我们还要进 Java 代码看一眼
先看接口定义,找出映射文件中的 condition 和 orderByClause 是从哪里来的:
List<PmsPriceDetail> selectByExample(xxxxExample example);
那它俩自然是来自于 xxxxExample 了,进类定义继续找:
public class xxxxExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
...
public static class Criterion {
private String condition;
...
}
}
出现了,分别是 Example 和其静态内部类的属性,那么 condition 的值又是在哪设的呢?
以方法 andIdEqualTo 为例:
public Criteria andIdEqualTo(Long value) {
addCriterion("id =", value, "id");
return (Criteria) this;
}
...
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
throw new RuntimeException("Value for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value));
}
...
public static class Criterion {
...
protected Criterion(String condition, Object value) {
this(condition, value, null);
}
...
protected Criterion(String condition, Object value, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.typeHandler = typeHandler;
if (value instanceof List<?>) {
this.listValue = true;
} else {
this.singleValue = true;
}
}
}
破案!condition 的值来自于 xxxxExample 中的固定调用,与输入参数完全无关,可以放心了
此外,可以看到带 condition 传参的 addCriterion 方法是 protected 的,这里不建议将此方法改为 public 或通过反射调用,会有注入风险
最后,再看 orderByClause 的值是在哪设的:
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
所以说设置排序从句确实是将传参拼接进去了,那么在业务层调用方法设置顺序时,就一定不要使用来自前端的参数,最好是定个枚举来用,这里就不细说了