Spring Boot项目SQL注入漏洞深度剖析:从CVE-2024-24112看MyBatis安全编码
1. 项目概述一次典型的后台管理系统漏洞挖掘最近在梳理一些开源项目的安全状况时XMall这个基于Spring Boot的电商后台管理系统进入了我的视野。作为一个在Java Web安全领域摸爬滚打了十来年的老手我习惯性地会去翻翻它的代码看看有没有什么“老朋友”在等着。果不其一个编号为CVE-2024-24112的SQL注入漏洞被我揪了出来。这个漏洞的成因非常典型属于那种在快速开发中容易被忽略但危害极大的安全问题。它发生在用户管理模块的列表查询接口里攻击者可以通过精心构造的请求参数直接操纵后台数据库查询轻则窃取管理员账号、订单信息重则可能导致整个数据库被拖库甚至服务器被控制。今天我就来带大家完整地复盘一下这个漏洞的发现、分析与验证过程希望能给各位开发者和安全研究员提个醒在追求功能实现的同时千万别忘了给安全上一把锁。2. 漏洞环境搭建与初步复现2.1 目标系统与漏洞版本定位XMall是一个前后端分离的分布式电商系统后端采用Spring Boot、MyBatis-Plus等技术栈前端使用Vue。根据CVE信息受影响的版本主要集中在某个特定commit之前的版本。为了复现我们需要先搭建一个漏洞环境。我选择从项目的GitHub仓库拉取存在漏洞的版本代码。这里有个小技巧不要直接克隆主分支而是根据CVE描述或漏洞提交记录找到对应的漏洞版本commit hash使用git checkout commit-hash切换到那个时间点的代码。这样可以确保我们分析的环境与漏洞描述完全一致。搭建环境的过程就是标准的Spring Boot项目启动流程导入IDE如IntelliJ IDEA等待Maven依赖下载完成。检查并配置application.yml中的数据库连接信息确保连上一个测试用的MySQL数据库。执行项目SQL目录下的初始化脚本创建表结构和默认数据。直接运行主启动类启动后端服务。启动成功后我们可以通过Swagger UI如果项目集成了的话或者直接构造HTTP请求来访问后端API。漏洞点通常隐藏在某个需要权限的API里所以你可能还需要模拟登录获取一个有效的JWT Token用于后续的授权请求。2.2 漏洞接口定位与手动测试根据公开的漏洞概要漏洞出现在用户管理相关的列表查询接口。通过翻阅代码或使用Burp Suite等工具抓取前端请求我们可以定位到类似/admin/user/list这样的接口它通常接受分页参数如page,size和查询条件参数。手动测试SQL注入的第一步永远是“试探”。我常用的方法是参数值后追加一个单引号‘。比如正常的请求是GET /admin/user/list?usernameadmin那么我们就测试GET /admin/user/list?usernameadmin。如果页面返回了数据库错误信息如MySQL的“You have an error in your SQL syntax”那基本就坐实了存在注入点。但更常见的情况是开发者配置了统一的异常处理返回一个模糊的错误页面或状态码500。这时我们需要更精细的判断方法比如使用逻辑真/假测试逻辑真测试usernameadmin AND 11。这相当于在原始查询条件后追加了一个永真条件如果页面返回正常用户列表说明我们追加的SQL片段被执行了。逻辑假测试usernameadmin AND 12。这是一个永假条件如果页面返回空列表或与真测试时明显不同的结果进一步确认了注入的存在。在XMall的这个案例中通过对username、email等查询字段进行上述测试我很快确认了注入点。服务器虽然没有直接报错但返回的数据内容随着我注入的永真、永假条件发生了明显变化这就是典型的“基于布尔盲注”的特征——通过页面返回结果的差异来推断SQL执行结果。3. 漏洞原理深度剖析3.1 问题代码追踪MyBatis-Plus与XML配置的隐患找到现象后下一步就是深入代码找到漏洞的根源。我直接全局搜索处理/admin/user/list这个请求的Controller和方法。在Spring Boot项目中这通常是一个带有RestController和RequestMapping注解的类。定位到Controller方法后发现它调用了一个Service层的方法来获取用户列表。继续跟进最终会到达Mapper层。XMall使用了MyBatis-Plus作为ORM框架查询构造方式有两种使用MyBatis-Plus提供的条件构造器如QueryWrapper或直接在XML映射文件中编写SQL。而问题恰恰出在XML映射文件的SQL编写上我找到了对应的Mapper XML文件查看其中的select语句。一段危险的代码出现了select idselectUserList resultMapUserResult SELECT u.user_id, u.username, u.email, ... FROM sys_user u WHERE u.del_flag 0 if testusername ! null and username ! AND u.username LIKE CONCAT(%, #{username}, %) /if if testemail ! null and email ! AND u.email LIKE CONCAT(%, #{email}, %) /if !-- 注意下面这行 -- if testparams.beginTime ! null and params.beginTime ! AND DATE_FORMAT(u.create_time, %Y-%m-%d) #{params.beginTime} /if /select猛一看似乎都使用了#{}占位符这是MyBatis预编译的方式能有效防止SQL注入。但是请仔细看最后关于时间查询的条件。这里有一个致命的错误它使用了字符串拼接来构造SQL的日期函数部分。在实际的漏洞版本中代码可能是这样的if testparams.beginTime ! null and params.beginTime ! AND u.create_time STR_TO_DATE(#{params.beginTime}, %Y-%m-%d) /if甚至是错误地直接拼接了${params.beginTime}这是原生的字符串替换极度危险。但核心问题在于开发者意图是进行日期范围过滤但处理日期格式转换或比较时如果参数校验不严就可能为SQL注入打开缺口。例如攻击者传入的params.beginTime不是”2023-01-01″而是”2023-01-01′ OR ‘1’’1″如果后端没有进行严格的格式校验和过滤这个值被直接拼接到SQL语句中就会破坏原有查询逻辑。3.2 预编译失效的临界点这里必须深入理解MyBatis中#{}和${}的区别这是Java Web安全必修课#{}是预编译处理。MyBatis会将其替换为?然后使用PreparedStatement的set方法按类型赋值。传入的字符串参数会被自动转义例如单引号会被转义为\从根本上杜绝了SQL注入。${}是字符串替换。MyBatis会直接将参数值替换到SQL语句中不做任何处理。如果参数来自用户输入就等于直接将攻击代码写入了SQL语句。那么为什么看起来用了#{}还可能出事呢关键在于#{}占位符所在的位置。预编译只能保证“值”的安全不能保证“SQL关键字或结构”的安全。举个例子-- 安全的写法ORDER BY字段是固定的 ORDER BY create_time #{sortOrder} -- 错误这里#{}试图替换的是关键字ASC/DESC但预编译的?不能放在这里。 -- 正确的安全写法需要业务层判断或者用${}但必须严格白名单过滤 ORDER BY create_time ${sortOrder} -- 危险如果sortOrder是用户可控的传入asc; DROP TABLE users --就完了。在XMall的漏洞中问题可能更隐蔽。比如开发者本意是想动态拼接日期格式字符串或者在某些复杂的动态查询条件下如多条件排序、动态表名不恰当地混合使用了#{}和字符串拼接或者错误地认为在LIKE语句中CONCAT(‘%’, #{param}, ‘%’)是绝对安全的这本身确实是安全的但却忽略了参数传入前在Service层或Controller层可能已经过了一些不安全的字符串处理。3.3 攻击载荷Payload构造分析假设漏洞点就在params.beginTime这个参数上并且后端代码存在将用户输入直接拼接进SQL函数如STR_TO_DATE,DATE_FORMAT参数中的情况。攻击者可以如何利用一个基础的测试载荷可能是params.beginTime 2024-01-01′) OR SLEEP(5) —分解一下这个Payload2024-01-01’先闭合原本的日期字符串。)闭合STR_TO_DATE或DATE_FORMAT函数的括号如果原SQL是这种结构。OR SLEEP(5)注入一个新的条件。SLEEP(5)是MySQL的函数会让数据库睡眠5秒。如果页面响应确实延迟了5秒就证明了注入的存在以及我们可以执行任意SQL函数。–注意有个空格这是MySQL的单行注释符用于注释掉原SQL语句中后续可能存在的其他字符比如另一个单引号确保我们注入的语句语法正确。通过这个时间盲注的Payload攻击者可以逐步“询问”数据库信息… OR (SELECT SUBSTRING(database(),1,1)’a’ AND SLEEP(5)) —判断数据库名第一个字母是否为’a’。利用SUBSTRING,ASCII,IF,SLEEP等函数的组合可以像剥洋葱一样逐位猜解出数据库名、表名、字段名最终窃取所有数据。注意在实际攻击中攻击者会使用自动化工具如sqlmap它能自动识别注入类型、数据库类型并利用各种技术布尔盲注、时间盲注、报错注入、联合查询注入高效地获取数据。手工注入主要用于原理理解和在工具受限时的测试。4. 漏洞利用链与潜在危害推演4.1 从注入点到数据泄露我们假设攻击者已经通过时间盲注或报错注入确认了注入点并判断出是MySQL数据库。接下来的利用链是标准化的获取当前数据库名利用SELECT DATABASE()函数。枚举所有数据库名查询information_schema.schemata表。枚举目标数据库中的所有表名查询information_schema.tables WHERE table_schema ‘当前数据库名’。选择感兴趣的表对于一个电商后台sys_user用户表、sys_order订单表、sys_product商品表无疑是高价值目标。枚举表的字段名查询information_schema.columns WHERE table_schema ‘当前数据库名’ AND table_name ‘目标表名’。拖取数据直接构造UNION SELECT查询或者继续用盲注逐条/逐批提取数据。通过这个过程攻击者可以拿到管理员账号密码虽然密码通常是加盐哈希存储的但如果哈希强度不够如MD5仍有被破解的风险。更可怕的是如果系统存在“记住我”或会话管理漏洞攻击者可能直接窃取到有效的会话令牌。用户个人信息姓名、电话、邮箱、地址这些可用于精准诈骗或社工攻击。订单数据包含商品、价格、收货地址等敏感商业信息和用户隐私。其他业务数据优惠券、库存、财务流水等。4.2 权限提升与服务器沦陷SQL注入的危害远不止数据泄露。如果数据库运行在较高权限下如root攻击者可能尝试进行权限提升最终控制服务器。利用INTO OUTFILE写文件如果MySQL的secure_file_priv配置允许攻击者可以执行SELECT ‘?php phpinfo(); ?’ INTO OUTFILE ‘/var/www/html/shell.php’将一个Web Shell写入网站目录从而获得代码执行能力。利用数据库存储过程或函数某些数据库支持通过SQL语句创建可执行系统命令的函数如MySQL的sys_exec但需要特定插件。一旦成功攻击者就能在服务器上执行任意命令。读取服务器敏感文件使用LOAD_FILE()函数读取服务器上的配置文件如/etc/passwd,~/.ssh/id_rsa获取进一步渗透的跳板。对于XMall这类Spring Boot应用攻击者可能会特别关注application.yml或application.properties文件因为其中可能包含数据库密码、第三方API密钥、加密密钥等最高机密。一旦这些信息泄露整个系统及其关联服务都可能失守。5. 漏洞修复方案与安全编码实践5.1 紧急修复与长期加固针对CVE-2024-24112这类SQL注入漏洞修复必须立即进行。紧急修复治标参数化查询确保Mapper XML中所有用户输入的地方都使用#{}预编译占位符。仔细审查所有动态SQL片段特别是涉及ORDER BY、GROUP BY、表名、列名拼接的地方。输入验证与过滤在Service层或Controller层对params.beginTime这类参数进行严格的格式校验。例如必须符合YYYY-MM-DD的正则表达式拒绝任何非预期字符。对于排序字段应使用白名单机制只允许”create_time”,”username”等预定义的字段名。升级依赖检查MyBatis-Plus等框架版本升级到最新稳定版以获取已知安全漏洞的修复。长期加固治本最小权限原则为应用程序数据库账户分配最小必要的权限通常只需要SELECT,INSERT,UPDATE,DELETE绝对不要使用root或具有FILE,PROCESS,SUPER等高级权限的账户连接数据库。使用更安全的查询方式尽量使用MyBatis-Plus的QueryWrapper或LambdaQueryWrapper进行条件构造它们内部对参数进行了安全处理。避免在XML中编写复杂的动态SQL如果必须务必使用MyBatis提供的where,if,choose等安全标签并坚持使用#{}。Web应用防火墙WAF在应用前端部署WAF可以拦截常见的SQL注入攻击特征作为一道额外的防线。定期安全审计与代码扫描将SQL注入检查纳入代码审查Code Review的 checklist。使用静态应用安全测试SAST工具如SonarQube, Fortify对代码库进行定期扫描。同时进行动态应用安全测试DAST模拟攻击者行为对线上应用进行测试。5.2 MyBatis/MyBatis-Plus安全编码规范结合这次漏洞我总结了几条在MyBatis/MyBatis-Plus项目中的安全编码铁律禁用${}除非是动态拼接完全可信的、非用户输入的内容如动态表名但表名来自配置文件否则在Mapper XML中严禁使用${}。对于排序等场景必须在Java代码层做白名单映射。// 错误示例危险 wrapper.orderByAsc(“${sortField}”); // 正确示例安全 MapString, String allowedSortFields new HashMap(); allowedSortFields.put(“name”, “username”); allowedSortFields.put(“time”, “create_time”); String dbField allowedSortFields.getOrDefault(userInputSortField, “create_time”); wrapper.orderByAsc(dbField);警惕IN语句使用IN查询时不要直接拼接字符串。应使用MyBatis的foreach标签配合#{}。!-- 安全写法 -- WHERE id IN foreach collectionids itemid open( separator, close) #{id} /foreachLIKE语句的正确写法使用CONCAT函数拼接通配符和参数或者使用MyBatis-Plus的like方法。// MyBatis-Plus 安全写法 wrapper.like(“username”, userInputName);!-- XML中安全写法 -- AND username LIKE CONCAT(‘%’, #{username}, ‘%’)对“用户输入”的定义要宽泛不仅指前端表单提交的数据URL参数、Cookie、HTTP头、甚至从其他系统接口获取的数据只要不是程序内部硬编码的都应视为不可信输入需要进行校验或使用预编译。6. 防御体系构建与安全开发生命周期SDLC一次漏洞的修复是点状的而构建持续的安全防御体系才是线状和面状的。对于企业和开发团队我强烈建议将安全融入整个软件开发生命周期SDLC。需求与设计阶段进行威胁建模识别系统中可能存在的安全威胁如数据泄露、身份冒用并在架构设计时考虑安全控制如API网关鉴权、数据加密存储。开发阶段安全培训让每一位开发者都具备基础的安全知识了解OWASP Top 10。安全组件/工具引入安全的基础库如用于密码哈希的BCrypt用于防CSRF的令牌机制。安全编码规范制定并强制执行团队的安全编码规范将SQL注入、XSS、命令注入等常见漏洞的防范措施写入规范。测试阶段自动化SAST/DAST扫描在CI/CD流水线中集成安全扫描工具每次代码提交或构建都自动进行安全检查发现问题及时阻断。人工渗透测试定期聘请专业的安全团队或白帽子进行渗透测试发现自动化工具难以发现的逻辑漏洞。部署与运维阶段安全配置确保服务器、中间件Nginx, Tomcat、数据库MySQL都按照安全基线进行了配置如关闭不必要的服务、修改默认端口、设置强密码。漏洞监控与应急响应订阅CVE漏洞通告关注使用框架和组件的安全动态。建立安全事件应急响应流程一旦出现漏洞能快速定位、修复和上线补丁。回过头看CVE-2024-24112它不仅仅是一个SQL注入漏洞的编号更是一个提醒在追求开发效率和功能丰富的道路上安全这根弦一刻也不能松。每一次不经意的字符串拼接每一个未经验证的用户输入都可能成为攻击者通往核心数据的捷径。作为开发者我们写的每一行代码都承载着守护用户数据和系统稳定的责任。多花几分钟进行安全校验使用预编译语句遵循最小权限原则这些看似微小的习惯汇聚起来就是一道坚固的安全防线。

相关新闻

MATLAB伪随机数生成:从种子控制到可重复性工程实践

MATLAB伪随机数生成:从种子控制到可重复性工程实践

1. 从一串数字说起:乱数、故事与MATLAB的奇妙关联看到标题[6 3 7 8 5 1 2 4 9 10],很多人可能会一头雾水,这看起来像是一个随机的数字序列。但如果你对MATLAB或者编程中的随机数生成稍有了解,再结合副标题“乱数にまつわるストーリ…

2026/6/24 21:26:17阅读更多 →
WSL2 Docker局域网访问全解:网络拓扑、路由配置与端口映射

WSL2 Docker局域网访问全解:网络拓扑、路由配置与端口映射

1. 为什么在 WSL 里装 Docker Engine 不是“装完就用”,而是个系统级工程?很多人点开这篇博文,是因为在 Windows 上敲下wsl --install后兴冲冲地跑进 Ubuntu,sudo apt install docker.io或者照着 Docker 官网的.deb包一顿操作&…

2026/6/24 21:20:48阅读更多 →
中间件漏洞复现实战:从原理到防御的完整闭环

中间件漏洞复现实战:从原理到防御的完整闭环

1. 项目概述:为什么我们要亲手复现中间件漏洞?在安全领域待久了,你会发现一个有趣的现象:很多安全工程师谈起漏洞原理头头是道,但真给他一个存在漏洞的环境,让他从零开始验证、利用,可能就卡壳了…

2026/6/24 21:20:48阅读更多 →
OpenClaw 核心原理:基于 openclaw.json 的技能调度中枢解析

OpenClaw 核心原理:基于 openclaw.json 的技能调度中枢解析

1. OpenClaw 不是“另一个多 Agent 框架”,而是面向真实工程交付的协作调度中枢OpenClaw 这个名字在最近三个月的技术社区里出现频率陡增,但绝大多数人第一次看到它时,下意识反应是:“又一个类似 LangChain 或 AutoGen 的多 Agent…

2026/6/24 22:57:51阅读更多 →
深入剖析MSC8254多核DSP:架构、高速接口与高密度通信处理实战

深入剖析MSC8254多核DSP:架构、高速接口与高密度通信处理实战

1. MSC8254:为高密度通信处理而生的多核DSP引擎在通信基础设施、多媒体网关和无线基站这些对数据处理吞吐量和实时性要求近乎苛刻的领域,一颗芯片的性能与架构直接决定了整个系统的能力边界。飞思卡尔(现为NXP的一部分)的MSC8254&…

2026/6/24 22:57:51阅读更多 →
Python网页链接批量抓取实战:从requests到并发处理的完整解决方案

Python网页链接批量抓取实战:从requests到并发处理的完整解决方案

1. 项目概述:从网页列表中批量提取链接 在数据驱动的时代,从互联网上高效、准确地收集信息是一项基础且关键的能力。无论是市场研究、竞品分析、内容聚合,还是构建自己的知识图谱,我们常常面临一个看似简单却暗藏玄机的任务&#…

2026/6/24 22:57:51阅读更多 →
Pytest迁移实战:提升可读性、可维护性与可调试性的测试工程化路径

Pytest迁移实战:提升可读性、可维护性与可调试性的测试工程化路径

1. 为什么我放弃unittest,把整个测试团队迁移到Pytest三年前,我们团队还在用unittest写自动化测试脚本。每次新增一个测试用例,都要写三遍:setUp、test_xxx、tearDown;参数化要靠ddt或自己手写for循环;失败…

2026/6/24 22:57:51阅读更多 →
Ubuntu下部署OpenClaw智能体框架实战指南

Ubuntu下部署OpenClaw智能体框架实战指南

1. OpenClaw 是什么,为什么要在 Ubuntu 上部署它OpenClaw 这个名字最近在自动化与智能体开发圈里出现得越来越频繁,但很多人第一次看到时会下意识联想到“开源的机械爪”或者某个硬件项目——其实完全不是。OpenClaw 是一个面向本地化、轻量级智能体&…

2026/6/24 22:57:51阅读更多 →
OpenAI内容审核API高级应用:从原理到生产级策略实战

OpenAI内容审核API高级应用:从原理到生产级策略实战

1. 项目概述:深入理解OpenAI内容审核API的高级应用在构建任何涉及用户生成内容的在线平台时,内容审核都是一个无法绕开的、至关重要的环节。无论是社区论坛、社交应用还是电商评论区,确保内容安全、合规、友善,不仅是法律和平台规…

2026/6/24 22:52:50阅读更多 →
【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体 一文搞定到底什么是智能体【人工智能】一文搞定到底什么是智能体一. LM,WorkFlow,Agent分别有什么么不同二. Agent的思考过程是怎样的三. Agent的五个核心部分1)LLM2)Prompt3)Me…

2026/6/24 7:33:03阅读更多 →
嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

1. 嵌入式GUI控件:从原理到实战的深度解析在嵌入式系统开发中,图形用户界面(GUI)的设计与实现往往是项目从“能用”到“好用”的关键一跃。不同于资源充沛的PC或移动平台,嵌入式设备的GUI需要在有限的CPU性能、内存空间…

2026/6/24 2:12:09阅读更多 →
Google AI Studio 300美元额度的真相与实战指南

Google AI Studio 300美元额度的真相与实战指南

1. 这300美金不是“送钱”,而是Google埋下的第一道技术门槛 你看到标题里那个醒目的“$300美金”时,第一反应可能是:又一个免费额度?领完就完事?我亲手试过——这300美金根本不是红包,而是一张入场券&…

2026/6/24 7:37:00阅读更多 →
TaskJuggler脚本编程入门:用代码实现自动化项目管理

TaskJuggler脚本编程入门:用代码实现自动化项目管理

TaskJuggler脚本编程入门:用代码实现自动化项目管理 【免费下载链接】TaskJuggler TaskJuggler - Project Management beyond Gantt chart drawing 项目地址: https://gitcode.com/gh_mirrors/ta/TaskJuggler TaskJuggler是一款强大的开源项目管理工具&#…

2026/6/24 0:02:41阅读更多 →
终极教程:使用angular-mobile-nav实现流畅的移动页面过渡效果

终极教程:使用angular-mobile-nav实现流畅的移动页面过渡效果

终极教程:使用angular-mobile-nav实现流畅的移动页面过渡效果 【免费下载链接】angular-mobile-nav An angular navigation service for mobile applications 项目地址: https://gitcode.com/gh_mirrors/an/angular-mobile-nav angular-mobile-nav是一款专为…

2026/6/24 0:02:41阅读更多 →
Wan2.1-Fun-V1.1-1.3B-InP Web UI使用教程:无需代码的AI视频创作

Wan2.1-Fun-V1.1-1.3B-InP Web UI使用教程:无需代码的AI视频创作

Wan2.1-Fun-V1.1-1.3B-InP Web UI使用教程:无需代码的AI视频创作 【免费下载链接】Wan2.1-Fun-V1.1-1.3B-InP 项目地址: https://ai.gitcode.com/hf_mirrors/PAI/Wan2.1-Fun-V1.1-1.3B-InP Wan2.1-Fun-V1.1-1.3B-InP是一款强大的AI视频创作工具,…

2026/6/24 0:02:41阅读更多 →