一、URLConnection简介
Java 中的 URLConnection
类(位于 java.net
包)是用于建立与由 URL(如 http://
、ftp://
等)标识的网络资源的连接、并与其进行通信的基础抽象类。 它允许 Java 程序读取远程资源(如网页文件、API 数据)或向远程服务发送数据(如 POST 表单)。URLConnection
本身代表一个网络连接对象,通过 URL.openConnection()
方法获取。它提供了设置请求头(setRequestProperty
)、获取响应头(getHeaderField
)、建立输入流(getInputStream
)读取响应内容、建立输出流(getOutputStream
)发送请求体、配置超时时间等功能。其子类 HttpURLConnection
则专门针对 HTTP/HTTPS 协议进行了增强,提供了状态码、请求方法(GET/POST)等更精细的控制。
核心要点概括:
- 用途: 在 Java 程序中访问和操作网络资源(如 URL 指向的文件、服务、数据)。
- 功能:
- 连接建立: 代表到 URL 标识资源的连接。
- 请求配置: 设置请求属性(如请求头、超时)。
- 数据传输:
- 读取响应数据(通过输入流)。
- 发送请求数据(通过输出流)。
- 信息获取: 访问响应信息(如响应头)。
- 获取方式: 通过
URL
对象的.openConnection()
方法。 - 抽象性: 是通用基础类,支持多种协议(实际协议行为由其实现或子类处理)。
- 子类化:
HttpURLConnection
是其最常用的子类,专门处理 HTTP(S) 请求。
在java中,Java抽象出来了一个URLConnection
类,它用来表示应用程序以及与URL建立通信连接的所有类的超类,通过URL
类中的openConnection
方法获取到URLConnection
的类对象。
Java中URLConnection支持的协议可以在sun.net.www.protocol
看到。
由上图可以看到,支持的协议有以下几个(当前jdk版本:1.7.0_80):
file ftp mailto http https jar netdoc gopher
虽然看到有gopher
,但是gopher
实际在jdk8版本以后被阉割了,jdk7高版本虽然存在,但是需要设置。具体可以看 https://bugzilla.redhat.com/show_bug.cgi?id=865541
以及http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/rev/8067bdeb4e31
。
其中每个协议都有一个Handle
,Handle
定义了这个协议如何去打开一个连接。
二、URLConnection案例
我们来使用URL发起一个简单的请求
public class URLConnectionDemo {
public static void main(String[] args) throws IOException {
URL url = new URL("https://www.baidu.com");
// 打开和url之间的连接
URLConnection connection = url.openConnection();
// 设置请求参数
connection.setRequestProperty("user-agent", "javasec");
connection.setConnectTimeout(1000);
connection.setReadTimeout(1000);
...
// 建立实际连接
connection.connect();
// 获取响应头字段信息列表
connection.getHeaderFields();
// 获取URL响应
connection.getInputStream();
StringBuilder response = new StringBuilder();
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
response.append("/n").append(line);
}
System.out.print(response.toString());
}
}
大概描述一下这个过程,首先使用URL建立一个对象,调用url
对象中的openConnection
来获取一个URLConnection
的实例,然后通过在URLConnection
设置各种请求参数以及一些配置,在使用其中的connect
方法来发起请求,然后在调用getInputStream
来获请求的响应流。 这是一个基本的请求到响应的过程。
三、SSRF
SSRF(Server-side Request Forge, 服务端请求伪造)。 由攻击者构造的攻击链接传给服务端执行造成的漏洞,一般用来在外网探测或攻击内网服务。
SSRF漏洞形成的原因大部分是因为服务端提供了可以从其他服务器获取资源的功能,然而并没有对用户的输入以及发起请求的url进行过滤&限制,从而导致了ssrf的漏洞。
通常ssrf容易出现的功能点如下面几种场景
- 抓取用户输入图片的地址并且本地化存储
- 从远程服务器请求资源
- 对外发起网络请求
- ……
黑客在使用ssrf漏洞的时候,大部分是用来读取文件内容或者对内网服务端口探测,或者在域环境情况下且是win主机下进行ntlmrelay攻击。
URL url = new URL(url);
URLConnection connection = url.openConnection();
connection.connect();
connection.getInputStream();
StringBuilder response = new StringBuilder();
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
response.append("/n").append(line);
}
System.out.print(response.toString());
比如上面代码中的url
可控,那么将url参数传入为file:///etc/passwd
URL url = new URL("file:///etc/passwd");
URLConnection connection = url.openConnection();
connection.connect();
...
以上代码运行以后则会读取本地/etc/passwd
文件的内容。
但是如果上述代码中将url.openConnection()
返回的对象强转为HttpURLConnection
,则会抛出如下异常
Exception in thread "main" java.lang.ClassCastException: sun.net.www.protocol.file.FileURLConnection cannot be cast to java.net.HttpURLConnection
由此看来,ssrf漏洞也对使用不同类发起的url请求也是有所区别的,如果是URLConnection|URL
发起的请求,那么对于上文中所提到的所有protocol
都支持,但是如果经过二次包装或者其他的一些类发出的请求,比如
HttpURLConnection
HttpClient
Request
okhttp
……
那么只支持发起http|https
协议,否则会抛出异常。
如果传入的是http://192.168.xx.xx:80
,且192.168.xx.xx
的80
端口存在的,则会将其网页源码输出出来
但如果是非web端口的服务,则会爆出Invalid Http response
或Connection reset
异常。如果能将此异常抛出来,那么就可以对内网所有服务端口进行探测。
java中默认对(http|https)做了一些事情,比如:
- 默认启用了透明NTLM认证
- 默认跟随跳转
关于NTLM认证的过程这边不在复述,大家可以看该文章《Ghidra 从 XXE 到 RCE》 默认跟随跳转这其中有一个坑点,就是
它会对跟随跳转的url进行协议判断,所以Java的SSRF漏洞利用方式整体比较有限。
- 利用file协议读取文件内容(仅限使用
URLConnection|URL
发起的请求) - 利用http 进行内网web服务端口探测
- 利用http 进行内网非web服务端口探测(如果将异常抛出来的情况下)
- 利用http进行ntlmrelay攻击(仅限
HttpURLConnection
或者二次包装HttpURLConnection
并未复写AuthenticationInfo
方法的对象)
对于防御ssrf漏洞的攻击,不单单要对传入的协议进行判断过滤,也要对其中访问的地址进行限制过滤。
参考
- https://www.javasec.org/ 【java安全】