导读:本期聚焦于小伙伴创作的《如何应对MyBatis-Plus中动态排序SQL注入?使用白名单机制限制排序字段》,敬请观看详情。把前端传来的字段名直接拼进MyBatis-Plus的orderBy语句,等于给攻击者留了后门。曾有一个后台接口仅用反射判断字段是否存在就拼串,结果被注入“sleep(5)”拖垮数据库。白名单机制的核心是先定义允许排序的字段集合,任何请求携带的排序项都必须经过校验,不在集合内直接拒绝或忽略。配合MyBatis-Plus的Wrapper,可以用枚举或配置表维护可信字段,再在Service层统一拦截。这样既能保留动态排序的灵活性,又彻底切断注入路径。下文给出具体实现与对比方案。

在后台管理系统中,列表查询几乎都离不开动态排序功能。MyBatis-Plus提供了非常便捷的Wrapper条件构造器,让开发者可以用代码方式拼装SQL。但如果排序字段来自前端请求且未加限制,就极易产生SQL注入漏洞。本文围绕如何利用白名单机制安全地实现动态排序展开说明。

如何应对MyBatis-Plus中动态排序SQL注入?使用白名单机制限制排序字段

一、动态排序为何会产生SQL注入

MyBatis-Plus的orderBy系列方法在底层会将字段名直接拼接进SQL语句。很多开发者为了省事,会把前端传来的sortField参数直接传入,例如orderBy(true, isAsc, sortField)。由于MyBatis-Plus不会对字段名做语法校验,攻击者完全可以传入类似id; drop table user;这样的内容,造成极其严重的后果。

更隐蔽的攻击方式是利用排序字段注入子查询或函数,比如传入(case when (select count(*) from admin)=1 then id else name end)。这类 payload 在日志里看起来只是一个字段名,却能探测数据库结构。因此,只要排序字段是外部输入,就必须视为不可信数据。

二、白名单机制的设计思路

白名单机制的本质是:系统预先声明哪些字段允许被排序,运行時只接受白名单内的字段,其余一律拒绝。这与黑名单不同,黑名单试图列举危险字符,永远防不住新变种;白名单则缩小可信范围,安全性更高。

在MyBatis-Plus项目中,白名单可以用枚举、常量类或数据库配置表来维护。推荐将白名单与实体类的真实列名绑定,避免拼写错误。同时,排序方向(升序或降序)也应限制为固定值,不能由用户自由输入任意字符串。

三、基于枚举的白名单实现示例

下面给出一个使用Java枚举维护白名单字段,并在Service层做校验的完整示例。该方案不依赖任何额外组件,适合大多数Spring Boot项目。

// 定义允许排序的字段白名单
public enum AllowedSortField {
    ID("id"),
    USERNAME("username"),
    CREATE_TIME("create_time");

    private final String column;

    AllowedSortField(String column) {
        this.column = column;
    }

    public String getColumn() {
        return column;
    }

    // 根据前端传入的名称匹配,找不到返回null
    public static String from(String fieldName) {
        if (fieldName == null) {
            return null;
        }
        for (AllowedSortField f : values()) {
            if (f.name().equalsIgnoreCase(fieldName) || f.column.equals(fieldName)) {
                return f.column;
            }
        }
        return null;
    }
}

// Service层使用方式
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    public List<User> listBySort(String field, String order) {
        // 校验字段是否在白名单
        String safeField = AllowedSortField.from(field);
        if (safeField == null) {
            // 不在白名单,使用默认排序或抛异常
            safeField = "id";
        }
        // 校验排序方向
        boolean isAsc = !"desc".equalsIgnoreCase(order);

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.orderBy(true, isAsc, safeField);
        return list(wrapper);
    }
}

上述代码中,AllowedSortField.from方法负责将任意前端参数转换为安全的列名,转换失败就降级为默认字段。这样即便攻击者传入恶意字符串,也只会落到id上,不会拼进SQL造成注入。

这种写法的优点是逻辑清晰、易于单测。如果以后表结构变更,只需修改枚举,不会遗漏校验点。相比在每一个Controller里写判断,集中到Service或独立校验工具类更利于维护。

四、与MyBatis-Plus分页插件结合

实际项目中动态排序常和分页一起出现。MyBatis-Plus的分页对象Page支持直接设置排序,但原理同上,字段仍需白名单过滤。

public IPage<User> pageBySort(long current, long size, String field, String order) {
    String safeField = AllowedSortField.from(field);
    if (safeField == null) {
        safeField = "id";
    }
    boolean isAsc = !"desc".equalsIgnoreCase(order);

    Page<User> page = new Page<>(current, size);
    page.addOrder(new OrderItem(safeField, isAsc));
    return page(page);
}

使用OrderItem时,字段名同样不能信任。上面代码在构造OrderItem之前已经完成白名单转换,保证进入分页插件的只能是合法列。

如果团队使用了代码生成器,也可以把白名单枚举一并生成,从源头保证实体字段和排序白名单一致,减少人工同步成本。

五、常见误区与补充建议

有的开发者认为只要用SqlInjector或MyBatis-Plus的防注入插件就万事大吉,其实那些主要拦截的是恶意表达式和危险关键字,对排序字段名这种“看起来合法”的输入无能为力。白名单是目前最稳妥的做法。

另外,不要在前端用下拉框隐藏真实字段名就以为安全,攻击者可以直接抓包改参数。所有校验必须在服务端完成。若系统排序规则复杂,可将白名单放入配置中心,支持热更新,无需发版即可调整可信字段。

方案安全性维护成本适用场景
直接拼接前端字段极低内部无外网系统(仍不推荐)
黑名单过滤字符中低遗留系统快速修补
枚举白名单绝大多数业务系统
配置表白名单字段多且常变的大型系统

通过白名单机制限制排序字段,可以在保留动态排序能力的同时,彻底封堵MyBatis-Plus动态排序带来的SQL注入风险。建议在新项目搭建时就将排序字段校验作为规范固定下来。

MyBatis-PlusSQL注入白名单机制修改时间:2026-08-10 04:54:30

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。