SSRF in Java
字数 930 2025-08-29 08:31:41
Java SSRF漏洞分析与防护指南
1. SSRF漏洞概述
SSRF(Server-side Request Forge, 服务端请求伪造)是一种由攻击者构造恶意链接传给服务端执行造成的漏洞,主要用于外网探测或攻击内网服务。
2. Java网络请求支持的协议
Java不像PHP有cURL,支持的协议需要通过以下方式检测:
- 代码中遍历协议
- 官方文档查看
- 通过
import sun.net.www.protocol查看
Java支持以下协议:
- file
- ftp
- mailto
- http
- https
- jar
- netdoc
3. Java中发起网络请求的类
3.1 仅支持HTTP/HTTPS协议的类
- HttpClient
- Request (HttpClient封装类)
- HttpURLConnection
- okhttp
3.2 支持所有协议的类
- URLConnection
- URL
4. 典型漏洞代码示例
@RequestMapping("/download")
@ResponseBody
public void downLoadImg(HttpServletRequest request, HttpServletResponse response) throws IOException{
try {
String url = request.getParameter("url");
if (StringUtils.isBlank(url)) {
throw new IllegalArgumentException("url异常");
}
downLoadImg(response, url);
}catch (Exception e) {
throw new IllegalArgumentException("异常");
}
}
private void downLoadImg (HttpServletResponse response, String url) throws IOException {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
String downLoadImgFileName = Files.getNameWithoutExtension(url)+"."+Files.getFileExtension(url);
response.setHeader("content-disposition", "attachment;fileName=" + downLoadImgFileName);
URL u;
int length;
byte[] bytes = new byte[1024];
u = new URL(url);
inputStream = u.openStream();
outputStream = response.getOutputStream();
while ((length = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, length);
}
}catch (Exception e) {
e.printStackTrace();
}finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
5. SSRF利用方式
5.1 直接利用
-
利用file协议读取任意文件:
curl -v 'http://localhost:8080/download?url=file:///etc/passwd' -
利用http/https协议进行端口探测
5.2 302跳转尝试
Java对302跳转有以下限制:
- 实际跳转的url必须在支持的协议内
- 传入的url协议必须和重定向的url协议一致
因此无法通过302跳转到gopher协议绕过限制。
6. 白盒检测规则
检测以下四种情况的代码:
- Request类发起请求:
Request.Get(url).execute()
- URL类的openStream方法:
URL u = new URL(url);
inputStream = u.openStream();
- HttpClient发起请求:
String url = "http://127.0.0.1";
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = client.execute(httpGet);
- URLConnection和HttpURLConnection:
URLConnection urlConnection = url.openConnection();
HttpURLConnection urlConnection = url.openConnection();
7. 漏洞修复方案
7.1 修复原则
- 限制协议为HTTP、HTTPS协议
- 禁止URL传入内网IP或设置URL白名单
- 无需限制302重定向
7.2 修复代码实现
添加Guava库依赖(用于获取一级域名):
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
验证逻辑:
String[] urlwhitelist = {"joychou.org", "joychou.me"};
if (!securitySSRFUrlCheck(url, urlwhitelist)) {
return;
}
安全验证函数:
public static Boolean securitySSRFUrlCheck(String url, String[] urlwhitelist) {
try {
URL u = new URL(url);
// 只允许http和https的协议通过
if (!u.getProtocol().startsWith("http") && !u.getProtocol().startsWith("https")) {
return false;
}
// 获取域名,并转为小写
String host = u.getHost().toLowerCase();
// 获取一级域名
String rootDomain = InternetDomainName.from(host).topPrivateDomain().toString();
for (String whiteurl: urlwhitelist){
if (rootDomain.equals(whiteurl)) {
return true;
}
}
return false;
} catch (Exception e) {
return false;
}
}
8. 总结
Java SSRF漏洞的主要特点:
- 支持的协议有限,但包含危险的file协议
- 302跳转有严格限制,难以利用跳转绕过
- 主要风险来自URL和URLConnection类的使用
- 修复方案应结合协议限制和域名白名单
通过严格的协议限制和域名白名单验证,可以有效防护Java应用中的SSRF漏洞。