1. Java代码审计实战:从SpEL注入到文件上传漏洞全解析
作为一名长期从事Java安全审计的工程师,我经常需要对企业级应用进行全面的安全检测。今天我将分享一套完整的Java代码审计案例集,涵盖从表达式注入到文件上传等12类常见漏洞。这些案例均来自真实项目,每个漏洞点都配有详细的技术分析和修复方案。
1.1 SpEL表达式注入漏洞剖析
Spring Expression Language(SpEL)是Spring框架中的表达式语言,它允许在运行时查询和操作对象图。虽然功能强大,但不当使用会导致严重的安全问题。
1.1.1 漏洞代码示例
java复制@GetMapping("/spel")
public String vul1(String exec) {
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext evaluationContext = new StandardEvaluationContext();
String result = "";
try {
result = parser.parseExpression(exec).getValue(evaluationContext).toString();
return result;
} catch (SpelParseException e) {
return "error";
}
}
这段代码直接使用用户输入的exec参数作为SpEL表达式进行解析,攻击者可以构造恶意表达式执行任意命令:
code复制T(java.lang.Runtime).getRuntime().exec('calc')
1.1.2 漏洞原理分析
StandardEvaluationContext提供了完整的SpEL功能,包括类型构造器、变量引用等- 攻击者可以通过
T()操作符访问任意Java类 - 结合Runtime.exec()方法可执行系统命令
1.1.3 修复方案
使用SimpleEvaluationContext替代StandardEvaluationContext:
java复制EvaluationContext evaluationContext = SimpleEvaluationContext.forReadOnlyDataBinding().build();
注意:SimpleEvaluationContext限制了表达式功能,禁止了类引用等危险操作
1.2 SQL注入漏洞深度解析
SQL注入是最常见的Web安全漏洞之一,下面分析几种典型的Java SQL注入案例。
1.2.1 MyBatis中的$与#区别
java复制@Select("select * from user_info where id = ${id}")
List<UserPrimary> findById(@Param("id") String id);
使用${}会直接拼接SQL语句,导致注入漏洞。正确的做法是使用#{}:
java复制@Select("select * from user_info where id = #{id}")
List<UserPrimary> findById(@Param("id") String id);
1.2.2 注入攻击示例
攻击者可以构造如下payload进行注入:
code复制1 or 1=1
1 and (select if(mid(user(),1,4)='root',sleep(1),123))
1.2.3 防御措施
- 始终使用预编译语句(#{})
- 使用MyBatis的拦截器进行SQL过滤
- 实施最小权限原则,数据库用户只赋予必要权限
1.3 URL重定向漏洞分析
不安全的URL重定向可能导致钓鱼攻击,以下是几种常见的漏洞模式。
1.3.1 无过滤重定向
java复制@GetMapping("/1")
public String vul(String url) {
return "redirect:" + url;
}
攻击者可以构造恶意URL诱导用户访问钓鱼网站。
1.3.2 白名单绕过技巧
java复制@GetMapping("/3")
public String vul3(String url) {
String host = new URL(url).getHost();
if (host.endsWith(".ffffffff0x.com")){
return "redirect:https://" + host;
}
}
攻击者可以通过以下方式绕过检查:
code复制http://www.baidu.com\www.ffffffff0x.com
1.3.3 安全重定向实现
java复制@GetMapping("/safe")
public String safe(String url) {
url = url.replaceAll("[\\\\#]","/");
String host = new URL(url).getHost();
if (host.endsWith(".ffffffff0x.com")){
return "redirect:https://" + host;
}
}
1.4 SSRF漏洞攻防实战
服务器端请求伪造(SSRF)允许攻击者通过服务器发起任意网络请求。
1.4.1 无过滤SSRF
java复制@GetMapping("/1")
public String vul1(String url) {
return Http.URLConnection(url);
}
攻击者可以访问内网服务或本地文件:
code复制file:///etc/passwd
http://169.254.169.254/latest/meta-data/
1.4.2 重定向绕过防护
java复制@GetMapping("/2")
public String vul2(String url) {
if (!Security.isIntranet(url)) {
return Http.URLConnection2(url);
}
}
攻击者可以搭建一个重定向服务器:
python复制from flask import Flask, redirect
app = Flask(__name__)
@app.route('/')
def redirect_to_file():
return redirect('file:///etc/passwd', code=302)
1.4.3 安全SSRF实现
java复制public static String HTTPURLConnection(String url) {
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setInstanceFollowRedirects(false); // 禁止重定向
// 其他安全检查...
}
1.5 SSTI模板注入漏洞
服务器端模板注入(SSTI)允许攻击者在模板引擎中执行任意代码。
1.5.1 Thymeleaf预处理注入
html复制<h1 th:href="@{__${name}__}">name1参数</h1>
攻击payload:
code复制__${T(java.lang.Runtime).getRuntime().exec('calc')}__
1.5.2 视图名称可控注入
java复制@GetMapping("/2")
public String path(@RequestParam String name) {
return "user/" + name + "/welcome";
}
攻击payload:
code复制__${T(java.lang.Runtime).getRuntime().exec('calc')}__::x
1.5.3 防御措施
- 避免用户输入直接作为模板内容
- 使用Thymeleaf的文本输出模式:
th:text="${name}" - 对用户输入进行严格过滤
1.6 文件上传漏洞全解
文件上传功能是Web应用常见的安全风险点,下面分析几种典型漏洞。
1.6.1 无过滤上传
java复制@PostMapping("/upload")
public String vul1(@RequestPart MultipartFile file) {
String fileName = file.getOriginalFilename();
Files.copy(file.getInputStream(), new File(path + fileName));
}
攻击者可以直接上传.jsp文件获取webshell。
1.6.2 黑名单绕过
java复制List<String> suffixlist = List.of(".jsp", ".jspx");
String suffix = fileName.substring(fileName.lastIndexOf("."));
if (!suffixlist.contains(suffix)) {
// 允许上传
}
攻击者可以使用大小写变种(.JSP)或特殊扩展名(.jspx)绕过。
1.6.3 路径穿越攻击
java复制String fileName = file.getOriginalFilename();
Files.copy(file.getInputStream(), new File(path + fileName));
攻击者可以构造文件名实现目录穿越:
code复制../../../etc/cron.d/malicious
1.6.4 安全上传实现
java复制@PostMapping("upload/safe")
public String safe(@RequestPart MultipartFile file) {
String suffix = fileName.substring(fileName.lastIndexOf("."));
if(!".xlsx".equals(suffix)&&!".xls".equals(suffix)){
return "非法文件类型";
}
String filePath = path + randomfilename + suffix;
Files.copy(file.getInputStream(), new File(filePath));
}
关键安全措施:
- 使用白名单验证文件类型
- 重命名上传文件
- 限制文件存储目录权限
1.7 信息泄露防护
敏感信息泄露是常见的安全问题,开发中需要注意:
html复制<p>
SecretId : AKIDf9NL2Rxx1LNxxqmSr0sxxoZ3XXXGNDxx
<br>
SecretKey : GI3XkgMlsiIabLxxvZw3sxxhQx6XXXxx
</p>
防护建议:
- 不要在代码或页面中硬编码敏感信息
- 使用配置中心管理密钥
- 定期轮换密钥
- 实施最小权限原则
1.8 IP伪造防护
获取客户端IP时需要考虑代理情况:
java复制@GetMapping("/realIp")
public static String ip(HttpServletRequest request) {
String ip1 = request.getRemoteAddr();
String ip2 = request.getHeader("X-Real-IP");
String ip3 = request.getHeader("X-Forwarded-For");
// 优先使用代理头中的IP
}
安全建议:
- 不要完全信任HTTP头中的IP信息
- 在代理层验证X-Forwarded-For头
- 对关键操作实施二次认证
2. Java安全开发最佳实践
基于以上漏洞分析,我总结出以下Java安全开发规范:
2.1 输入验证原则
- 实施白名单验证而非黑名单
- 对特殊字符进行转义处理
- 使用类型安全的参数绑定
2.2 安全编码规范
- 避免动态拼接SQL、命令、表达式等
- 使用预编译语句处理数据库操作
- 对文件操作实施严格的路径检查
2.3 安全配置建议
- 禁用不必要的功能(如SpEL的高级特性)
- 实施最小权限原则
- 定期更新依赖库,修复已知漏洞
2.4 审计与监控
- 记录关键操作的日志
- 实施异常行为监控
- 定期进行安全代码审计
在实际开发中,安全应该贯穿整个软件开发生命周期。每个功能点的设计阶段就应该考虑安全因素,而不是在开发完成后才进行安全加固。通过建立完善的安全开发流程,可以大幅降低应用的安全风险。