
MyBatis-Plus 踩坑Lambda、QueryWrapper 隐藏的 SQL 注入风险摘要MyBatis-Plus简称 MP作为 MyBatis 的增强工具简化 CRUD 开发很多开发认为 MP 自带封装天然不会产生 SQL 注入。但在代码审计和 SRC 实战挖掘中大量 Java 项目使用 MP 依然爆出 SQL 注入。LambdaQueryWrapper 相对安全但是 QueryWrapper、自定义 apply、last、inSql 等方法一旦传入用户可控参数极易触发注入漏洞。本文区分 MP 安全写法与危险 API结合真实漏洞案例讲解底层原理、审计思路与修复方案适合开发自查和白帽代码审计。本文仅用于授权代码审计、安全学习禁止用于未授权系统渗透。免责声明本文技术内容仅用于网络安全学习、代码审计、授权范围内安全评估。未经目标系统所有者书面许可禁止对任何系统进行探测、渗透测试。违规操作带来的全部法律责任由操作者自行承担。0x00 前言日常代码审计经常遇到开发这样说“我们用的是 MyBatis-Plus不用写 XML框架帮我们处理参数不存在 SQL 注入。”这个认知存在巨大误区。MP 只是封装了 MyBatis底层依然是 MyBatis。安全与否取决于你调用的是哪一类 API以及参数来源是否用户可控。简单一句话先划重点✅ LambdaQueryWrapper 的eq、like、ge、le这类普通条件构造方法底层走预编译相对安全❌ QueryWrapper、apply()、last()、inSql()、getSqlSelect()等方法直接字符串拼接 SQL传入前端可控参数会产生 SQL 注入。0x01 底层原理MP 两种参数处理逻辑MyBatis-Plus 构造 SQL 分为两种模式占位符参数绑定模式安全LambdaQueryWrapper 的eq(实体字段, 值)QueryWrapper 的eq(字段名, 值)。框架内部会把值处理为 JDBC?占位符底层使用PreparedStatement预编译用户输入仅作为参数值无法破坏 SQL 语法和 MyBatis#{}原理一致。示例安全代码// 安全写法value会作为预编译参数 LambdaQueryWrapperUser wrapper Wrappers.lambdaQuery(); wrapper.eq(User::getId, id); wrapper.like(User::getUsername, username); ListUser userList userMapper.selectList(wrapper);无论前端传入什么特殊字符只会当成字符串值不会拼接进 SQL 结构。原生字符串直接拼接高危apply()、last()、inSql()这类 APIMP 不会做预编译占位处理直接把传入的字符串拼入最终 SQL等价于 MyBatis${}。如果字符串内容来自前端用户可控输入直接触发 SQL 注入。0x02 高危 API 逐个拆解实战审计重点2.1 last () 函数最容易踩坑的注入点last(String sqlSegment)追加 SQL 语句到末尾常用于 limit 分页。MP 直接把字符串拼在 SQL 尾部无预编译。危险示例// 高危pageSize来自前端可控参数 QueryWrapperUser wrapper Wrappers.query(); wrapper.eq(status,1).last(limit pageSize); userMapper.selectList(wrapper);前端传入pageSize1 or 11拼接后的 SQLSELECT id,username FROM user WHERE status1 limit 1 or 11直接绕过 limit 限制查询全表数据。重要提醒很多开发为了分页偷懒直接拼接 limit 参数这是 MP 项目里最高发的注入漏洞。✅安全方案分页请使用 MP 内置 Page 对象不要手动 last 拼接 limit。安全分页写法// 推荐参数自动预编译不会字符串拼接 PageUser page new Page(current, pageSize); LambdaQueryWrapperUser wrapper Wrappers.lambdaQuery(); wrapper.eq(User::getStatus,1); userMapper.selectPage(page, wrapper);2.2 apply () 自定义条件注入apply(String sqlSegment, Object... params)有两个重载不带参数的重载方法最危险。危险写法无参数直接拼接// 高危name前端可控 wrapper.apply(username name);传入 Payloadnametest or 11SQL 闭合成功注入生效。✅安全写法使用占位符版本第二个参数传值// 安全{0} 会被处理为预编译参数 wrapper.apply(username {0}, name);审计重点看到 apply 只有一个字符串参数并且内容拼接了前端变量判定高危。2.3 inSql () 子查询注入inSql(String column, String inSql)第二个参数直接拼接 SQL 子查询。// 高危deptIdStr前端可控 wrapper.inSql(dept_id, deptIdStr);前端传入select id from user拼接 SQLselect * from user where dept_id in (select id from user)攻击者可构造子查询查询其他表敏感数据。注意inSql 第二个参数属于 SQL 片段拼接不是值列表不要直接接收用户输入。如果业务需要 in 查询优先使用in(dept_id, valueList)该方法走预编译。2.4 QueryWrapper 字符串字段名 用户可控字段名QueryWrapper 可以直接指定字符串字段名若字段名由前端传入存在注入风险// 危险sortField 前端可控 wrapper.orderByAsc(sortField);底层会直接拼接字段名等价于 MyBatis${sortField}。LambdaQueryWrapper 用实体方法引用User::getUsername字段名写死在代码不存在这个风险。结论动态排序如果必须使用字符串字段后端必须增加白名单校验不能直接接收前端字段名称。0x03 容易混淆的坑LambdaQueryWrapper 就一定绝对安全很多人认为 Lambda 写法完全没有风险这个结论不完全正确。Lambda 只是字段名写死避免了字段可控带来的注入但如果在 Lambda 内部调用apply()、last()依旧存在注入危险示例LambdaQueryWrapperUser wrapper Wrappers.lambdaQuery(); wrapper.eq(User::getStatus,1) // 依然高危last内部字符串直接拼接 .last(limit pageSize);哪怕前面全是 Lambda 写法只要调用高危 API 并且拼接用户可控变量漏洞依旧存在。Lambda 只是保护字段名不会保护 apply、last 这类自定义 SQL 片段。0x04 实战 SRC 漏洞案例业务接口接收前端参数 pageSize开发使用 last 拼接 limit 实现分页。GetMapping(/user/list) public ListUser getUserList(String pageSize){ QueryWrapperUser wrapper Wrappers.query(); wrapper.eq(del_flag,0).last(limit pageSize); return userMapper.selectList(wrapper); }PayloadpageSize1;update user set passwordmd5(123456) where id1;--如果数据库支持多语句执行可直接更新数据即使禁止多语句也可以用union查询其他表数据。审计要点搜索项目中.last(关键词查看后面是否拼接外部传入变量。0x05 代码审计快速排查清单白帽挖洞思路审计 MP 项目优先全局搜索下面关键词.last(—— 重点检查字符串是否拼接前端入参.apply(—— 区分单参数重载和带占位符重载.inSql(—— 第二个参数来源是否用户可控.orderByAsc(/.orderByDesc(—— 参数是字符串变量时检查白名单.setSqlSelect(—— 自定义查询字段可控字段名存在注入审计判断逻辑方法内拼接的 SQL 片段如果变量来自前端 HTTP 入参且无白名单、无过滤 → 高危 SQL 注入。小提示MyBatis-Plus 3.x、3.5.x 版本都存在该特性不是版本 bug属于框架设计特性。0x06 安全开发规范与修复方案优先使用 LambdaQueryWrapper使用实体方法引用指定字段避免硬编码字符串字段分页查询不要手写 last 拼接 limit使用 MP 内置 Page 分页对象尽量不使用last()、不带占位符的apply()、inSql()业务必须使用时禁止直接拼接用户输入动态排序、动态字段场景字段名做后端白名单不在白名单直接拒绝禁止直接把 HTTP 请求参数拼入任何 SQL 片段数据库账号最小权限限制 information_schema 访问禁止多语句执行代码扫描工具全局检索.last(、.apply(做安全 CR 评审。0x07 总结MyBatis-Plus 不是 SQL 注入的 “免死金牌”安全取决于 API 选择LambdaQueryWrapper 的普通条件 eq、like、ge 等底层预编译相对安全last()、无占位符apply()、inSql()、动态字符串排序字段属于直接 SQL 拼接可控入参传入会产生注入Lambda 只是锁定字段名如果链式调用 last/apply漏洞依旧存在代码审计 MP 项目优先检索高危 API追溯参数来源是 Java Web 挖掘 SQL 注入的高效手段。最后关于网络安全技术储备学好网络安全不论是就业还是做副业赚钱都不错但要学会网络安全还是要有一个学习规划。最后大家分享一份全套的网络安全学习资料给那些想学习网络安全的小伙伴们一点帮助对于0基础小白入门如果你是零基础小白想快速入门网络安全是可以考虑的。一方面是学习时间相对较短学习内容更全面更集中。二方面是可以找到适合自己的学习方案包括网安成长学习路线图、SRC黑客文档、护网行动、黑客必读书单、面试题、学习视频等教程。带你从零基础系统性的学好网络安全需要的可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】1.成长路线图学习规划要学习一门新的技术作为新手一定要先学习成长路线图方向不对努力白费。对于从来没有接触过网络安全的同学我们帮你准备了详细的学习成长路线图学习规划。可以说是最科学最系统的学习路线大家跟着这个大的方向学习准没问题。2.网安入门到进阶视频教程很多朋友都不喜欢晦涩的文字我也为大家准备了视频教程其中一共有21个章节每个章节都是当前板块的精华浓缩。全套教程文末领取哈3.SRC黑客文档大家最喜欢也是最关心的SRC技术文籍黑客技术也有收录SRC技术文籍黑客资料由于是敏感资源这里不能直接展示哦全套教程文末领取哈4.护网行动资料其中关于HW护网行动也准备了对应的资料这些内容可相当于比赛的金手指5.黑客必读书单随着互联网技术的飞速发展网络安全已经成为了当今科技领域的一大热点。这些SQL注入、CCNA、Web渗透、Linux服务器等以其强大的语言理解和防御能力正在守护着我们网络世界。 那以下这些PDF籍就是非常不错的学习资源。6.网络安全岗面试题合集当你自学到这里你就要开始思考找工作的事情了而工作绕不开的就是真题和面试题。这份完整版的网络安全学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】