章
目
录
学过mybatis的同学应该都知道,为了避免SQL注入,mybatis语句中要尽量使用 #{xxx}
来避免SQL注入,而尽量不要使用${xxx}
,原因也很简单,就是因为${xxx}
这样格式的参数会直接参与SQL 编译,该参数内容会直接影响SQL语句结构,从而无法避免注入攻击,而#{xxx}
参数只会作为字符串参数进行拼接,再执行编译,不会影响SQL原本结构。
既然如此,那为何还要有${xxx}
这种简单的占位符呢?原因在于有一种特殊情况,当涉及到动态表名和列名时,只能使用${xxx}
这样的参数格式。为了SQL防止注入,我们只能通过手工处理入参来进行防止注入操作。
案例
比较常见的案例就是根据不同的字段进行排序,排序方式也可以动态实现升序和降序。
解决方法
以下是一个相关实现mybatis使用${}避免SQL注入的案例方法。
1)test表
这里假设我们test表有param1,param2,param3这3个字段。
2)TestEntity
该表对应的TestEntity自然也会包含param1,param2,param3这3个属性。
3)入参
比如现在根据动态字段进行排序,查询入参如下:
@Data
public class QueryRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 查询字段1
*/
private String param1;
/**
* 排序字段
*/
private String orderField;
/**
* 排序类型
*/
private String orderType;
}
4)Mapper.xml里的SQL
我们Mapper.xml查询SQL如下:
select * from test
<where>
// 这里省略...
</where>
order by ${orderField
} ${orderType
}
我们发现这里使用了${orderField}/${orderType}
存在SQL注入风险,如果sortField传一个注入语句那就危险了,因此我们在Service或者Controller层对orderField/orderType的取值要进行限制为param1或param2或param3或asc或desc,即TestEntity中的字段以及排序类型。
5)实现限制方法
我们利用反射写一个通用的限制方法如下(这里还允许升降序的取值限制):
public class DynamicOrderUtils {
private static final List<String> ORDER_TYPES = Arrays.asList("descend", "ascend", "desc", "asc");
/**
* 获取对象可以排序的属性值列表
*
* @param object
* @return
*/
private static Set<String> getTableFields(Class<?> object) {
//获取filed数组
Set<String> resultList = new HashSet<>();
try {
Field[] fields = object.getDeclaredFields();
for (Field field : fields) {
resultList.add(field.getName());
}
return resultList;
} catch (Exception e) {
e.printStackTrace();
}
return resultList;
}
/**
* 判断动态order是否是合理
*
* @param object
* @return
*/
public static boolean isRight(String orderColumn, String orderType, Class<?> object) {
//先获取所有可用排序属性
Set<String> columnSet = getTableFields(object);
return (StringUtils.isBlank(orderColumn) || columnSet.contains(orderColumn)) && (StringUtils.isBlank(orderType) || ORDER_TYPES
.contains(orderType.toLowerCase()));
}
}
6)使用校验
最后我们在Service或者Controller层对orderField/orderType
的取值要进行限制,只需调用如下方法:
boolean right = DynamicOrderUtils.isRight(queryRequest.getOrderField(), queryRequest.getOrderType(), TestEntity.class);
如果返回true,说明满足条件,否则你可以抛出异常提示。
总结
以上就是mybatis使用${}如何避免SQL注入的全部内容,目前还没有更好的解决方案,如果你有更好的方案,欢迎留言!