Java后端代码审计的核心不是拿着工具全项目扫描一遍然后看报告,那种做法只能发现表层问题。真正的安全隐患排查要从数据流入口开始,沿着参数传递、业务逻辑、持久化、外部交互的路径逐层追踪。很多严重漏洞恰恰出现在框架已经做了基础过滤、但业务代码又进行了二次拼接或类型转换的位置。审计时先定位所有外部输入入口,包括Controller参数、Request直接读取、文件上传、消息队列消费端、定时任务读取的配置或数据,然后追踪这些数据在后续代码中的每一次变形和使用场景。

SQL注入仍然是Java后端最高发的严重漏洞之一。虽然MyBatis的#{}预编译参数能防住大部分注入,但${}拼接、Order By字段拼接、动态表名、Like模糊查询中的手写百分号、以及部分老项目中的Statement直接执行,都是高危点。审计时不能只看Mapper XML,还要检查Service层是否把用户输入传入了${}占位符。一个典型的问题代码是:

String keyword = request.getParameter("keyword");
List<User> users = mapper.findUsers("%" + keyword + "%");

如果Mapper里写的是like #{keyword},这里是安全的;但如果写的是like '%${keyword}%',就存在注入。更隐蔽的情况是MyBatis的order by ${sortField},因为排序字段无法用预编译参数,必须做白名单校验。审计时要确认sortField是否在允许的字段集合内,而不是简单判断非空。

命令执行漏洞在Java后端中常出现在调用Runtime.exec、ProcessBuilder、以及一些框架的表达式执行功能里。重点排查用户输入是否进入了命令字符串或表达式字符串。例如使用Runtime.exec时,很多人以为传入字符串数组就安全,但如果数组中的某个元素仍然包含用户可控的拼接内容,同样可以注入额外参数。审计时还要关注OGNL、SpEL、MVEL这类表达式引擎的调用,特别是Struts2历史漏洞、Spring框架中@Value注解结合外部输入的场景。任何将外部参数拼进表达式字符串再执行的操作都应视为高危。

文件上传漏洞的审计不能只检查扩展名白名单。很多系统做了前端校验或简单的Content-Type判断,但后端没有重新校验文件头、没有限制上传目录的执行权限、没有重命名文件。攻击者可以上传一个扩展名为jpg但内容为JSP脚本的文件,如果上传目录在Web根目录下且服务器解析JSP,就能直接获取WebShell。审计时要确认上传后的文件存储路径是否在静态资源目录之外,或者是否通过Nginx等中间件限制了该目录的脚本执行。另外,压缩包上传后的解压操作也要检查是否存在Zip Slip路径穿越,解压时是否对../进行了过滤。

反序列化漏洞是Java生态中非常特殊的一类问题。Java反序列化不只是一个简单的数据恢复过程,它会触发对象的readObject、readResolve等方法,如果类路径上存在可利用的Gadget链,攻击者就能实现远程代码执行。审计时重点查找ObjectInputStream.readObject、ObjectInputStream.readUnshared、以及Fastjson、Jackson等JSON库在特定配置下的反序列化调用。对于Fastjson,要检查是否开启了autoType;对于Jackson,要确认是否对多态类型做了严格限制。很多项目在接收外部JSON数据时直接转换成Object或泛型类型,如果底层库存在绕过,就会成为入口。建议在审计时关注pom.xml或gradle依赖中是否存在已知漏洞版本的组件,同时检查代码中是否有自定义的readObject实现。

越权漏洞在业务系统中往往比注入类漏洞更常见,也更难通过自动化工具发现。水平越权审计的核心是检查每个数据查询接口是否都校验了数据归属。例如用户A通过修改URL中的ID参数就能查看用户B的订单详情,说明后端只验证了登录态,没有验证资源归属。审计时要逐个检查Controller中接收资源ID的方法,看Service层是否通过当前登录用户的身份去过滤查询条件。垂直越权则要检查普通用户是否能调用管理员接口,重点看拦截器或过滤器的路径配置是否覆盖了所有管理端接口,以及注解权限校验是否被遗漏。

敏感信息泄露的排查范围很广。日志中打印用户密码、身份证号、银行卡号、token是常见问题。审计时搜索log.info、log.debug、System.out.println中是否直接输出了用户对象或请求参数。异常处理中如果直接返回e.getMessage(),可能把SQL语句、文件路径、内部类名暴露给前端。另外,接口返回的VO对象如果直接复用了数据库实体类,可能把不需要的字段一并返回。审计时要检查Controller的返回类型,确认是否对敏感字段做了脱敏或裁剪。

SSRF漏洞在Java后端中常出现在服务端发起的HTTP请求、文件下载、图片加载等功能中。如果用户传入的URL没有经过协议和地址校验,攻击者可以探测内网服务、读取云元数据、甚至攻击内部系统。审计时查找HttpURLConnection、RestTemplate、OkHttpClient、Apache HttpClient等HTTP客户端的调用位置,确认URL是否来自用户输入。即使URL来自服务端拼接,也要检查其中是否包含了用户可控的域名或IP部分。对于文件下载功能,要防止file://协议读取本地文件。

XXE漏洞在较老的Java项目中仍然存在。审计时查找DocumentBuilderFactory、SAXParserFactory、XMLInputFactory、TransformerFactory等XML解析器的使用位置,确认是否关闭了外部实体解析。很多开发者只记得设置setFeature禁用DOCTYPE,但忽略了setExpandEntityReferences(false)和setSupportDTD(false)。另外,如果项目使用了POI解析Excel、或处理SVG图片,也要检查底层XML解析是否安全。对于Java 8以上的环境,部分解析器默认关闭了外部实体,但审计时不能依赖默认配置,必须显式设置安全属性。

权限校验的绕过往往出现在过滤器或拦截器的逻辑缺陷中。例如使用request.getRequestURI().startsWith("/admin")来判断是否管理端请求,攻击者可以通过URL编码、路径参数、分号等方式绕过。审计时要检查权限过滤器的匹配逻辑是否严谨,是否使用了equals或精确前缀匹配,是否处理了..路径规范化问题。Spring Security的配置中,如果antMatchers的顺序不当,也可能导致某些路径被错误放行。另外,JWT或Session校验逻辑中,如果只验证了token的存在性而没有验证签名或过期时间,也是严重问题。

并发与竞态条件在业务安全中容易被忽视。例如优惠券领取、库存扣减、账户余额变动等操作,如果代码先查询再更新,且没有加锁或使用原子操作,就可能被并发请求绕过限制。审计时检查Service层中是否存在select后update的非原子操作,以及是否使用了synchronized但锁的范围不够。数据库层面可以检查是否使用了乐观锁或悲观锁,Redis层面可以检查是否使用了原子递减操作。这类问题在促销活动、秒杀场景中危害极大。

第三方组件漏洞的排查是代码审计中不可跳过的一环。很多Java项目依赖了几十个甚至上百个第三方库,其中任何一个存在已知漏洞都可能成为攻击入口。审计时不能只关注直接依赖,还要检查传递依赖。可以使用依赖分析工具生成依赖树,然后与已知漏洞库进行比对。重点关注的组件包括:Fastjson、Log4j、Shiro、Spring Framework、Struts2、XStream、Commons Collections、Commons FileUpload等。对于无法升级的组件,要检查代码中是否使用了存在漏洞的API,并评估是否可以通过配置或代码调整来缓解风险。

代码审计的最终产出不应该是简单的漏洞列表,而应该包含每个漏洞的触发路径、影响范围、修复建议和验证方法。对于Java后端项目,建议按照数据流分析的方式,从入口到出口完整走一遍关键业务链路。同时结合自动化扫描工具进行初筛,再用人工方式确认和深入挖掘。审计过程中要保持对业务逻辑的理解,很多看似安全的代码在特定业务场景下会变成漏洞。安全是一个持续的过程,代码审计只是其中一个环节,后续还需要配合安全测试、上线前检查和运行时监控才能形成完整的防护体系。