跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Java 代码审计:SSRF 漏洞原理与常见写法

SSRF(服务器端请求伪造)是一种由攻击者构造形成由服务器端发起请求的安全漏洞。在 Java 中,SSRF 支持 sun.net.www.protocol 下所有的协议,如 file、ftp、http、https 等。常见漏洞写法包括 urlConnection、HttpURLConnection、Apache HttpClient 及 ImageIO 等类未对 URL 参数进行过滤。源码审计时需重点关注能发起 HTTP 请求的类及函数,如 HttpClient.execute、URL.openStream 等,防止攻击者访问内网资源或读取本地敏感文件。

月亮邮递员发布于 2026/3/15更新于 2026/6/1232 浏览

SSRF(Server-Side Request Forgery)

SSRF(服务器端请求伪造)是一种由攻击者构造形成由服务器端发起请求的安全漏洞。攻击者能够利用受影响的应用程序,发送伪造的 HTTP 请求,使其伪装成服务器内部发起的请求,从而能够直接或间接地访问或控制应用程序无意中暴露的受保护资源。

Web 应用程序往往会提供一些能够从远程获取图片或是文件的接口,在这些接口上用户使用指定的 URL 便能完成远程获取图片、下载文件等操作。攻击者可以通过使用 file 协议来读取服务器本地 /etc/passwd 和 /proc/self/cmdline 等敏感文件,同时攻击者也可以利用被攻击的服务器绕过防火墙直接对处于内网的机器发起进一步的攻击。

原理

SSRF 漏洞的本质是服务端提供了从其他应用服务器获取数据的功能,但没有对目标地址做过滤与限制。攻击者通过向应用程序发送特定的请求,传入一个用户可控的参数作为目标 URL 或者 IP 地址,进行实现对受害系统内部任意资源的访问。

支持的协议

在 Java(JDK21)中,SSRF 支持 sun.net.www.protocol 下所有的协议:file、ftp、http、https、jar、jmod、jrt 及 mailto 协议。这些是 JDK 内置的、通过 URL 类直接支持的标准协议,但在生产代码中应始终使用标准的 java.net API,避免直接依赖 sun.* 包。

由于上述协议的限制,以及传入的 URL 协议必须和重定向后的 URL 协议一致的原因,使得 Java 中的 SSRF 并不能像 PHP 中一样使用 gopher 协议来拓展攻击面。

  • file - 本地文件系统协议
    • URL 示例:file:///etc/passwd
    • 作用:用于访问本地文件系统中的文件。
  • ftp - 文件传输协议
    • URL 示例:ftp://user:[email protected]/pub/file.txt
    • 作用:通过 FTP 协议下载或上传文件。
  • http - 超文本传输协议
    • URL 示例:http://example.com/index.html
    • 作用:获取 Web 资源。
  • https - 安全超文本传输协议
    • URL 示例:https://example.com/api/user
    • 作用:获取 Web 资源。
  • jar - JAR 文件协议
    • URL 示例:jar:file:///app/lib/utils.jar!/config.properties
    • 作用:访问 JAR 包内部的资源文件。
  • jmod - JMOD 文件协议(JDK 9+)
    • URL 示例:jmod:/path/to/module.jmod!/resources/file.txt
    • 作用:用于访问 .jmod 文件中的内容。
  • jrt - 运行时镜像协议(JDK 9+)
    • URL 示例:jrt:/java.base/java/lang/Object.class
    • 作用:访问运行时模块镜像(Run-Time Image)中的类和资源。
  • mailto - 邮件协议
    • URL 示例:mailto:[email protected]?subject=Hello&body=Hi!
    • 作用:启动默认邮件客户端,预填收件人、主题、正文等。

常见发起网络请求,并产生 SSRF 漏洞写法

urlConnection

URLConnection 类是 Java 中用于发起网络请求的基础类,它是一个抽象类,通过 java.net.URL 类的 openConnection() 方法来获取其实例。表示指向 URL 指定资源的活动链接,它有两个直接子类,分别是 HttpURLConnection 和 JarURLConnection。在默认情况下,urlConnection 的参数没有有效控制时会引起 SSRF 漏洞。

package com.ssrf.vuln.ssrfdemo.Servlet;

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

@WebServlet(name = "Ssrf1Servlet", urlPatterns = "/ssrf1")
public class Ssrf1Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("text/html; charset=utf-8");
        String urlString = req.getParameter("url");
        if (urlString == null || urlString.isEmpty()) {
            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            resp.getWriter().write("URL 参数不能为空");
            return;
        }
        try {
            // 1. 创建 URL 对象
            URL url = new URL(urlString);
            // 2. 打开连接,获取 URLConnection 对象
            URLConnection urlConnection = url.openConnection();
            // 3. 读取响应
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
            StringBuilder content = new StringBuilder();
            String line;
            while ((line = in.readLine()) != null) {
                content.append(line);
            }
            in.close();
            resp.getWriter().write(content.toString());
        } catch (IOException e) {
            resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            resp.getWriter().write("无法访问指定的 URL");
        }
    }
}

HttpURLConnection

HttpURLConnection 是 java.net 包中的一个类,它继承自 URLConnection 类,专门用于处理 HTTP 协议的连接。

它可以发送 GET 请求与 POST 请求。同样的,在没有过滤的默认情况下其会产生 SSRF 漏洞。

package com.ssrf.vuln.ssrfdemo.Servlet;

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

@WebServlet(name = "Ssrf2Servlet", urlPatterns = "/ssrf2")
public class Ssrf2Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("text/html; charset=utf-8");
        String urlString = req.getParameter("url");
        if (urlString == null || urlString.isEmpty()) {
            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            resp.getWriter().write("URL 参数不能为空");
            return;
        }
        try {
            // 创建 URL 对象
            URL url = new URL(urlString);
            // 打开连接,并转换为 HttpURLConnection
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            // 设置请求方法
            urlConnection.setRequestMethod("GET");
            // 设置连接超时时间
            urlConnection.setConnectTimeout(5000);
            // 设置读取超时时间
            urlConnection.setReadTimeout(5000);
            // 检查响应码
            int responseCode = urlConnection.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK) {
                resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
                resp.getWriter().write("无法访问指定的 URL,响应码:" + responseCode);
                return;
            }
            // 读取数据
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            StringBuilder content = new StringBuilder();
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                content.append(inputLine);
            }
            in.close();
            // 返回实际内容
            resp.getWriter().write(content.toString());
        } catch (Exception e) {
            // 捕获异常
            resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            resp.getWriter().write("发生错误:" + e.getMessage());
        }
    }
}

Apache HttpClient - Request

Request 是一个基于 Apache HttpClient 的高级封装,提供了更简洁、流畅的 API 来发起 HTTP 请求。在没有过滤的默认情况下会产生 SSRF 漏洞。

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5-fluent</artifactId>
    <version>5.5.1</version>
</dependency>
package com.ssrf.vuln.ssrfdemo.Servlet;

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.hc.client5.http.fluent.Content;
import org.apache.hc.client5.http.fluent.Request;
import java.io.IOException;

@WebServlet(name = "Ssrf3Servlet", urlPatterns = "/ssrf3")
public class Ssrf3Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String urlParam = req.getParameter("url");
        // 创建请求
        Request request = Request.get(urlParam);
        // 执行请求
        Content content = request.execute().returnContent();
        String responseBody = content.asString();
        // 输出响应
        resp.getWriter().write(responseBody);
    }
}

HttpClient

HttpClient 是 Apache Jakarta Common 下的一个流行的开源 HTTP 客户端库,它不仅支持 HTTP 协议,还支持 HTTPS、代理、Cookie 管理、身份验证等功能。在默认情况下,其也会产生 SSRF 漏洞。

package com.ssrf.vuln.ssrfdemo.Servlet;

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

@WebServlet(name = "Ssrf4Servlet", urlPatterns = "/ssrf4")
public class Ssrf4Servlet extends HttpServlet {
    private static final CloseableHttpClient httpClient = HttpClients.createDefault();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String urlParam = req.getParameter("url");
        // 创建请求
        HttpGet hg = new HttpGet(urlParam);
        // 执行请求
        CloseableHttpResponse hResp = httpClient.execute(hg);
        // 读取响应
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(hResp.getEntity().getContent()));
        String line;
        StringBuilder stringBuilder = new StringBuilder();
        while ((line = bufferedReader.readLine()) != null) {
            stringBuilder.append(line);
        }
        // 输出响应
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write(stringBuilder.toString());
    }
}

URL/openStream/ImageIO

java.net.URL 包是 Java 标准库中用于处理 URL 的类和接口的包。URL 是用于标识和定位资源的字符串,通常包括协议(如 HTTP、FTP 等)和资源的位置信息(如域名或 IP 地址、端口、路径等)。java.net.URL 包提供了创建、解析、查询和操作 URL 的类和方法。

通过 URL 对象的 openStream() 方法,能够得到指定资源的输入流。这时如果 URL 对象可控,则会产生 SSRF 漏洞。

ImageIO 是 Java 标准库 javax.imageio 包中的一个核心类,它提供了一组静态方法,用于执行图像的读取、写入、获取格式信息等基本 I/O 操作。

package com.ssrf.vuln.ssrfdemo.Controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.URL;

@RestController
public class UrlController {
    @GetMapping("/ssrf5")
    public String UrlOpenStream(@RequestParam String url) throws IOException {
        URL u = new URL(url);
        BufferedReader in = new BufferedReader(new java.io.InputStreamReader(u.openStream()));
        String inputLine;
        StringBuilder r = new StringBuilder();
        while ((inputLine = in.readLine()) != null) {
            r.append(inputLine);
        }
        in.close();
        return r.toString();
    }

    @GetMapping("/ssrf6")
    public String UrlImageIO(@RequestParam String url) throws IOException {
        URL u = new URL(url);
        Image image = ImageIO.read(u);
        return image.toString();
    }
}

源码审计关键词

SSRF 漏洞 URL 中常出现 url、f、file、page 等参数。程序会发起 HTTP 请求获取远程资源、分享、收藏等操作,因此代码审计时要特别留意能够发起 HTTP 请求的类及函数:

  • HttpClient.execute
  • HttpClient.executeMethod
  • HttpURLConnection.connect
  • HttpURLConnection.getInputStream
  • URL.openStream
  • URLConnection.getInputStream
  • Request.Get.execute
  • Request.Post.execute
  • ImageIO.read
  • OkHttpClient.newCall.execute
  • HttpServletRequest
  • BasicHttpRequest
  • HttpURLConnection.getInputStream

Solr SSRF 漏洞审计

漏洞背景简述

  • 漏洞编号:CVE-2021-27905
  • 影响版本:7.0.0 ~ 7.7.3, 8.0.0 ~ 8.8.1
  • solr 下载:https://archive.apache.org/dist/lucene/solr/

目录

  1. SSRF(Server-Side Request Forgery)
  2. 原理
  3. 支持的协议
  4. 常见发起网络请求,并产生 SSRF 漏洞写法
  5. urlConnection
  6. HttpURLConnection
  7. Apache HttpClient - Request
  8. HttpClient
  9. URL/openStream/ImageIO
  10. 源码审计关键词
  11. Solr SSRF 漏洞审计
  12. 漏洞背景简述
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Copilot Pro 使用指南:模型配额与选型策略
  • 执行式 AI 数据交互核心语法:Agent 架构与实现
  • 基于腾讯云轻量应用服务器部署 OpenClaw 并接入 QQ 与飞书机器人
  • OneClick-macOS-Simple-KVM 一键部署 macOS 虚拟机指南
  • 基于 AI 辅助的 Java 在线图书借阅平台开发实践
  • Arduino BLDC 驱动方案:MimiClaw 框架结合 ESP32 嵌入式机器人
  • OpenClaw 实战:构建具备自主执行能力的 AI 数字替身
  • 神经网络核心原理与入门指南
  • 鸿蒙应用性能优化与 Next 原生合规实践
  • Github Copilot 学生认证通过指南:2FA、材料与常见问题处理
  • Python 标准库与生态应用指南:从入门到机器学习实战
  • Windows 平台零基础部署 Qwen1.5 大模型教程
  • Java Graphics2D 基础图形绘制详解
  • 淘特 App x-sign 签名逆向分析:从抓包到算法还原
  • Ubuntu 安装硬盘分区方案与实践
  • JDK 安装与环境配置完整指南
  • Linux 进程替换详解:从 fork 到 exec 的完整链路
  • 龙虾机器人(OpenClaw)本地部署指南
  • MCP 工具速成:npx 与 uvx 全流程安装指南
  • Xapian: 一款 C++ 全文检索解决方案

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online