一文吃透 Java SSRF:原理 + 审计 + 绕过 + 防御

一文吃透 Java SSRF:原理 + 审计 + 绕过 + 防御

🛡️ Java SSRF 源码审计全解


🚩 什么是 SSRF?

SSRF(Server-Side Request Forgery,服务器端请求伪造),攻击者通过控制服务端发送请求目标,从而访问本应服务器内部可见的资源。


📌 审计原则:你应该关注哪里?

  1. 是否存在外部 HTTP 请求行为?
    • 关键词定位:URLConnection, HttpClient, RestTemplate, OkHttp 等。
  2. 请求地址是否来源于用户输入?
    • 用户输入识别点:request.getParameter(), @RequestParam
  3. 是否缺乏对请求地址的白名单/协议/IP 检查?
    • 包括:是否校验协议(是否只允许 HTTP)、是否禁止内网 IP、是否限制端口范围等。
  4. 是否允许重定向?
    • SSRF 重定向绕过校验是常见技巧。

✅ SSRF 漏洞类型与示例详解


1. java.net.URL / URLConnection

📂 漏洞代码

 @WebServlet("/fetch")
 public class SSRFDemoServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
 ​
         // 获取用户提供的 target 参数
         String target = request.getParameter("target");
 ​
         if (target == null || target.isEmpty()) {
             response.getWriter().println("请提供 ?target= 参数");
             return;
         }
 ​
         try {
             // 服务器端发起请求
             URL url = new URL(target);
             InputStream is = url.openStream();
             Scanner scanner = new Scanner(is);
             response.getWriter().println("远程内容读取成功:");
             while (scanner.hasNextLine()) {
                 response.getWriter().println(scanner.nextLine());
             }
         } catch (Exception e) {
             response.getWriter().println("请求失败:" + e.getMessage());
         }
     }
 }

💥利用方式

 http://yourserver.com/fetch?target=file:///etc/passwd

🔎 分析

  • 支持协议:http, https, ftp, file, jar
  • file:// 可用于读取服务器本地文件(如 /etc/passwd
  • http://127.0.0.1 可用于访问内网接口,如 admin 面板、元数据服务等

2. HttpURLConnection

📂 漏洞代码

 @WebServlet("/fetch")
 public class SSRFDemo extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
 ​
         String target = request.getParameter("url"); // 用户通过 URL 参数传入目标地址
 ​
         if (target == null || target.isEmpty()) {
             response.getWriter().println("请传入目标 URL,例如:/fetch?url=http://example.com");
             return;
         }
 ​
         try {
             URL url = new URL(target);
             HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 仅支持 HTTP/HTTPS
             conn.setRequestMethod("GET");
             conn.setConnectTimeout(3000);
             conn.setReadTimeout(3000);
 ​
             InputStream is = conn.getInputStream();
             Scanner scanner = new Scanner(is);
             response.getWriter().println("响应内容(前几行):");
             int count = 0;
             while (scanner.hasNextLine() && count++ < 5) {
                 response.getWriter().println(scanner.nextLine());
             }
 ​
         } catch (Exception e) {
             response.getWriter().println("请求失败: " + e.getMessage());
         }
     }
 }

💥 利用方式

🧬 SSRF 访问内网接口:
 GET /fetch?url=http://127.0.0.1:8080/admin

作用:绕过外部防火墙访问内部管理接口(如 Tomcat 后台、K8s API)。


☁️ SSRF 访问云元数据服务:
 GET /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/

作用:获取在 AWS 等云平台上的凭证,进一步获取 S3、RDS 访问权限。

🔍 分析

项目说明
✅ 协议限制使用 HttpURLConnection 限制了 file:// 等非 HTTP 协议
❌ IP/IP段未校验可以访问内网 IP,易被用来探测内网、访问云服务
❌ 没有域名/IP白名单可以访问任意公网地址,甚至 SSRF 转发
❌ 没有限流可用于自动化扫描
⚠️ 输出内容部分打印若返回的是 XML、配置、敏感内容,仍有泄露风险

3. Apache HttpClient(标准 + Fluent API)


✅ 场景 A:标准 Apache HttpClient(重定向隐患)

📂 漏洞代码
 @WebServlet("/fetch")
 public class SSRFDemoServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
 ​
         String targetUrl = request.getParameter("url");
 ​
         if (targetUrl == null || targetUrl.isEmpty()) {
             response.getWriter().println("请提供 url 参数,例如 /fetch?url=http://example.com");
             return;
         }
 ​
         try (CloseableHttpClient client = HttpClients.createDefault()) {
             HttpGet httpGet = new HttpGet(targetUrl);
             HttpResponse httpResponse = client.execute(httpGet);
 ​
             response.getWriter().println("响应内容(前几行):");
             try (InputStream content = httpResponse.getEntity().getContent();
                  Scanner scanner = new Scanner(content)) {
                 int lines = 0;
                 while (scanner.hasNextLine() && lines++ < 5) {
                     response.getWriter().println(scanner.nextLine());
                 }
             }
 ​
         } catch (Exception e) {
             response.getWriter().println("请求失败:" + e.getMessage());
         }
     }
 }
💥 利用方式

攻击者访问:

 http://vulnerable.com/fetch?url=http://attacker.com:8000/redirect
  1. 请求 http://attacker.com:8000/redirect ✅ 通过初始校验
  2. 响应返回 302 + Location: http://127.0.0.1:8080/admin
  3. Apache HttpClient 自动重定向 ➜ 访问内网接口
  4. SSRF 成功!
🔎 分析要点
  • 支持 HTTP/HTTPS(默认)
  • 自动跟随重定向,易被 SSRF 利用
  • 是企业中广泛使用的请求工具

✅ 场景 B:Apache HttpClient Fluent API(开发者最容易忽视)

📂 漏洞代码
 @WebServlet("/fast-fetch")
 public class SSRFFluentServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse response)
             throws IOException {
 ​
         String url = request.getParameter("url");
         if (url == null || url.isEmpty()) {
             response.getWriter().println("请提供 url 参数");
             return;
         }
 ​
         // 🧨 SSRF 高危调用:无过滤,直接使用用户 URL
         String result = Request.Get(url).execute().returnContent().asString();
         response.getWriter().println("获取结果:" + 
                            result.substring(0, Math.min(result.length(), 200)));
     }
 }
💥 利用方式
http://vulnerable.com/fast-fetch?url=http://169.254.169.254/latest/meta-data/

或者探测内网服务:

http://vulnerable.com/fast-fetch?url=http://127.0.0.1:8080/secret
🧪 利用效果
  • 可用来读取内网服务状态接口
  • 获取云平台凭据(如 AWS Metadata)
  • 可能绕过 WAF(短路径、快速请求)
🔍 分析
特点说明
API 风格简洁Request.Get(url).execute() 一行发请求
易忽略校验很多开发者以为是“高级封装”,忽略 URL 风险点
自动处理重定向默认会自动跟随 301/302
支持协议仅限 http://https://,不支持 file://

⚖️ 标准 vs Fluent 对比审计

比较点标准 HttpClientFluent API (Request.Get)
请求方式显式构造对象 (HttpGet, HttpPost)链式封装请求
易读性中等非常高,开发者更喜欢
SSRF 风险高(如果输入未过滤)更高(因为经常无任何校验)
支持协议HTTP/HTTPS,配置后支持其他协议仅 HTTP/HTTPS
自动重定向跟随默认开启默认开启
审计推荐关注点.execute(...) 入参为用户 URLRequest.Get(x) 中的 x 是否用户可控

4. Spring RestTemplate

📂 漏洞代码

@RestController
public class SSRFDemo {

    @GetMapping("/fetch")
    public String fetch(@RequestParam String url) {
        RestTemplate restTemplate = new RestTemplate();
        // 使用 RestTemplate 发送 HTTP 请求
        return restTemplate.getForObject(url, String.class);
    }
}

🔎 分析

  • Spring 项目常用
  • 底层使用 HttpURLConnectionHttpClient
    • 利用方式
      • 内网服务探测、访问云原数据服务
      • 自动重定向绕过
  • 容易隐藏在服务封装层内部(需审查封装逻辑)

5. OkHttpClient

📂 漏洞代码

@RestController
@RequestMapping("/fetch")
public class SSRFDemo {

    private final OkHttpClient client = new OkHttpClient();

    @GetMapping
    public String fetchData(@RequestParam String url) throws Exception {
        // 从 HTTP 请求的参数中获取 URL
        Request request = new Request.Builder().url(url).build();
        Response response = client.newCall(request).execute();
        return "HTTP Status Code: " + response.code();
    }
}

💥 利用方式

攻击者可以通过传入恶意的 URL,将请求转发到攻击者控制的外部服务器。例如:

http://victim.com/fetch?url=http://attacker.com/malicious

这会让服务器发起一个请求到 http://attacker.com/malicious,而攻击者能够控制这个外部地址,获取敏感信息或者发起攻击。

🔎 分析

  • 常用于微服务通信、移动端
  • 支持 DNS 解析缓存、重定向跟随,可做内网扫描跳板

6. 其他危险类支持多协议

类库协议支持风险
URLhttp, https, ftp, file, jar🔥 高
URLConnectionhttp, https, ftp, file, jar🔥 高
HttpClient默认 http/https⚠️ 中
RestTemplatehttp/https⚠️ 中高
WebClient(Reactor)http/https⚠️ 中
Jsoup.connect()http/https⚠️ 中
ImageIO.read(URL)http/file⚠️ 中
ClassLoader.getResource()file/jar⚠️ 低(信息泄露)

🧭 SSRF 漏洞审计关键词速查表

类别审计关键词
原始网络类new URL(, URL.openConnection,
URLConnection, openStream
HTTP 库HttpURLConnection, HttpClients.createDefault
, HttpGet, OkHttpClient
Spring 相关RestTemplate, WebClient,
getForObject, exchange
用户输入可控点request.getParameter, @RequestParam
资源类ImageIO.read, ClassLoader.getResource,
FileInputStream(new URL(...))

🛠️ 修复方式

  • URL 白名单校验 仅允许访问可信的主机或路径,如:
if (!url.startsWith("https://api.example.com")) {
throw new IllegalArgumentException("非法URL");
}
  • 禁止内网地址访问(防止访问 127.0.0.1192.168.0.0/16 10.0.0.0/8 等) 解析 IP 后进行判断:
InetAddress address = InetAddress.getByName(new URL(url).getHost());
if (address.isSiteLocalAddress() || address.isLoopbackAddress()) {
throw new SecurityException("禁止访问内网");
}
  • 禁止使用非 HTTP 协议(如 file://)
if (!url.startsWith("http://") && !url.startsWith("https://")) {
throw new IllegalArgumentException("仅允许 HTTP 请求");
}
  • 禁用重定向跟随
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setInstanceFollowRedirects(false);
  • 统一封装报错响应内容,可以返回自定义固定报错页面,避免过多信息返回

✅ 总结

  • SSRF 的核心风险在于攻击者可以控制服务端对内网、敏感资源的访问
  • 审计时需从 数据源(用户输入) -> 传输链路(函数调用) -> 汇聚点(请求发起) 进行溯源。
  • 非 HTTP 协议的支持是高风险关键点,应特别关注。
© 版权声明
THE END
喜欢就支持一下吧
点赞9赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容