mybatis使用${}如何避免SQL注入

后端 潘老师 6个月前 (10-17) 169 ℃ (0) 扫码查看

学过mybatis的同学应该都知道,为了避免SQL注入,mybatis语句中要尽量使用 #{xxx}来避免SQL注入,而尽量不要使用${xxx},原因也很简单,就是因为${xxx}这样格式的参数会直接参与SQL 编译,该参数内容会直接影响SQL语句结构,从而无法避免注入攻击,而#{xxx} 参数只会作为字符串参数进行拼接,再执行编译,不会影响SQL原本结构。

既然如此,那为何还要有${xxx} 这种简单的占位符呢?原因在于有一种特殊情况,当涉及到动态表名和列名时,只能使用${xxx}这样的参数格式。为了SQL防止注入,我们只能通过手工处理入参来进行防止注入操作。

案例

比较常见的案例就是根据不同的字段进行排序,排序方式也可以动态实现升序和降序。

解决方法

以下是一个相关实现mybatis使用${}避免SQL注入的案例方法。

核心思路:针对查询入参的动态字段,将其取值限制在SQL表对应的Entity的字段类,如果动态字段不在Entity内就说明是非法入参

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注入的全部内容,目前还没有更好的解决方案,如果你有更好的方案,欢迎留言!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/9715.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】