开发 & 安全 | SSRF[URLConnection] 与 HttpClient

admin 2024年11月28日00:35:05评论2 views字数 18692阅读62分18秒阅读模式

SSRF & 网络请求

前言

记载一下 SSRF, 顺便记载一下 HttpClient 发送 HTTP 包, 一方面是 SSRF 漏洞挖掘时会有这方面的涉及, 一方面在编写工具时难免会有发包需求.

URLConnection

URLConnection 是 JDK 自带的一个抽象类, 当然也是原生发送请求的一个类, 当然这个请求是通过什么协议具体也要看 URLConnection 的实现.

发送 HTTP 请求

在原生的 Java 程序中, 我们可以编写如下代码进行发送 HTTP 请求:

URL url = new URL("http://www.baidu.com/");
URLConnection urlConnection = url.openConnection();
try (
        // 实现了 AutoCloseable 接口, 可以在 try(...) 中进行定义流, 在使用完毕后会自动关闭流
        BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
) {
    StringBuilder res = new StringBuilder(); // 等待追加
    String tmp = "";
    while ((tmp = br.readLine()) != null) {
        res.append(tmp).append("n"); // 追加字符串信息
    }
    System.out.println(res); // 响应结果
}

这个案例很简单, 会直接向百度发送一次http请求, 并且通过流的方式将响应结果拿到了.

URL 初始化 & openConnection 运行逻辑

那么下面我们来看一下URL类做了一些什么事情, 并且是如何得到URLConnection的.

开发 & 安全 | SSRF[URLConnection] 与 HttpClient

这里最终会通过URL::getURLStreamHandler进行初始化handler成员属性, 那么我们看一下这个方法做了一些什么事情:

开发 & 安全 | SSRF[URLConnection] 与 HttpClient

而最后一行的return最终会设置到URL类的handler成员属性中, 并且调用其parseURL方法:

this.handler = handler;
handler.parseURL(this, spec, start, limit); // 对 URL 的解析, 就不再追源代码了

从代码中可以观察出如下三点:

  • 最终会实例化sun.net.www.protocol.当前协议.Handler类, 而sun.net.www.protocol这个包的定义我们可以在rt.jar中进行找到.
  • 这些Handler类都为URLStreamHandler(抽象类)实例.
  • 最终将生成的Handler实例放入到URL::handler成员属性中, 也会放入到URL::handlers这个Hashtable中.

那么我们可以看一下URL::openConnection方法的定义:

public URLConnection openConnection() throws java.io.IOException {
    return handler.openConnection(this); // 会调用到具体 Handler 的 openConnection 方法
}
public final InputStream openStream() throws java.io.IOException {
    return openConnection().getInputStream(); // 间接调用 openConnection
}

这些实际上是调用的Handler::openConnection方法, 具体逻辑如下:

开发 & 安全 | SSRF[URLConnection] 与 HttpClient

SSRF 利用

所以这里我们利用的协议是有限的, 通常可以利用如下协议:

file ftp mailto http https jar netdoc gopher(1.7可利用但需条件)

这里多了一个gopher万金油协议出来, 其原因则是, 在JDK1.7gopher是存在的, 但是需要具体的配置, 具体可以参考: https://bugzilla.redhat.com/show_bug.cgi?id=865541 中:

开发 & 安全 | SSRF[URLConnection] 与 HttpClient

而在JDK1.8中, 我们已经无法对其进行利用了, 所以在Java - SSRF漏洞利用中, 显得特别鸡肋, 它不能像PHP那么灵活, 在这里可利用的协议有: file, http协议.

file 协议

当然可以利用该协议进行读取文件信息:

URL url = new URL("file:///C:/Windows/win.ini");
URLConnection urlConnection = url.openConnection();
try (
        BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
) {
    StringBuilder res = new StringBuilder();
    String tmp = "";
    while ((tmp = br.readLine()) != null) {
        res.append(tmp); // 追加字符串信息
    }
    System.out.println(res); // ; for 16-bit app support[fonts][extensions][mci extensions][files][Mail]MAPI=1
}

PHP - SSRF - file协议利用的不同点是, 这里的file协议是可以进行列目录的, 编写如下代码进行测试:

URL url = new URL("file:///C:/"); // 列出 C 盘目录信息
URLConnection urlConnection = url.openConnection();
try (
        BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
) {
    StringBuilder res = new StringBuilder();
    String tmp = "";
    while ((tmp = br.readLine()) != null) {
        res.append(tmp).append("n"); // 追加字符串信息
    }
    System.out.println(res);
    /*
        $Recycle.Bin
        $WINRE_BACKUP_PARTITION.MARKER
        Documents and Settings
        DumpStack.log
        DumpStack.log.tmp
        hiberfil.sys
        Intel
        pagefile.sys
        PerfLogs
        ProgramData
        Program Files
        Program Files (x86)
        Recovery
        swapfile.sys
        System Volume Information
        Users
        Windows
        壁纸
    */
}

但如果被强制转换为HttpURLConnection, 那么我们就无法再利用file协议做一些事情了, 会抛出异常:

URL url = new URL("file:///C:/Windows/win.ini");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
/* 异常如下:
 java.lang.ClassCastException: sun.net.www.protocol.file.FileURLConnection cannot be cast to java.net.HttpURLConnection
*/

http 协议

在这里我们当然可以进行探测内网的http协议了, 在这里笔者开启了一个80端口的本地服务:

URL url = new URL("http://127.0.0.1:80/"); // 本地是开放端口的
URLConnection urlConnection = url.openConnection();
try (
        BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
) {
    StringBuilder res = new StringBuilder();
    String tmp = "";
    while ((tmp = br.readLine()) != null) {
        res.append(tmp).append("n");
    }
    System.out.println(res); // Hello World
}

当然笔者本地也开启了3306端口的一个mysql服务, 当代码逻辑中向外抛出了java.io.IOException: Invalid Http response异常时, 我们可以进行监听内网其他非WEB服务的端口:

URL url = new URL("http://127.0.0.1:3306/"); // 3306 是 MYSQL 服务
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try (
        BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8)); // java.io.IOException: Invalid Http response
) {
    StringBuilder res = new StringBuilder();
    String tmp = "";
    while ((tmp = br.readLine()) != null) {
        res.append(tmp).append("n");
    }
    System.out.println(res);
}

具体是否可以进行探测非web服务还需要其具体的代码逻辑是否向外抛出了异常才可以.

当然也可以进行ntlmrelay攻击, 参考: https://cloud.tencent.com.cn/developer/article/1937699 & https://xlab.tencent.com/cn/2019/03/18/ghidra-from-xxe-to-rce/

使用封装好的HttpClient, OkHttp只可以进行发送HTTP请求, 不允许使用file协议.

HttpClient

HttpClient 是 Apache Jakarta Common下的一个子项目, 它提供了高效的、最新的、功能丰富的HTTP协议客户端编程工具包.

不同类型请求方式

为了能看到完整报文, 在这里笔者使用 nc 进行监听, 看一下每次调用 API 的结果. 使用 nc 返回如下结果进行调试:

HTTP/1.1 200 OK
Date: Wed, 15 Nov 2023 06:25:24 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 11

HelloWorld!

当然使用 HttpClient 则需要引入对应依赖:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

get 请求

普通 GET 请求

准备如下代码:

// 准备要请求的 URL
String url = "http://127.0.0.1:2333/";
try (
        // 创建可关闭的 httpClient 客户端
        CloseableHttpClient httpClient = HttpClients.createDefault();
) {
    // 准备 GET 请求对象
    HttpGet httpGet = new HttpGet(url);
    try (
            // 准备响应对象
            CloseableHttpResponse response = httpClient.execute(httpGet);
    ) {
        // 得到响应结果
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
        // 打印响应结果
        System.out.println(result); // HelloWorld!
        // 关闭流
        EntityUtils.consume(entity);
    }
}

最终 nc 可以收到如下请求:

C:UsersAdministrator>nc -lvvp 2333
GET / HTTP/1.1
Host: 127.0.0.1:2333
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_131)
Accept-Encoding: gzip,deflate

HTTP/1.1 200 OK
Date: Wed, 15 Nov 2023 06:25:24 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 11

HelloWorld!
sent 123, rcvd 150

这里可以看到使用HttpClient发送的请求头.

伪造 Header 头

调用HttpGet::addHeader方法即可, 准备如下代码:

// 准备要请求的 URL
String url = "http://127.0.0.1:2333/";
// 创建可关闭的 httpClient 客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
// 准备 GET 请求对象
HttpGet httpGet = new HttpGet(url);
// 增加请求头
httpGet.addHeader("User-Agent""Tester By Heihu577");
httpGet.addHeader("Referer""localhost?");
// 准备响应对象
CloseableHttpResponse response = httpClient.execute(httpGet);
// 得到响应结果
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
// 打印响应结果
System.out.println(result); // HelloWorld!
// 关闭流
EntityUtils.consume(entity);
// 关闭结果
response.close();
// 关闭客户端
httpClient.close();

结果:

GET / HTTP/1.1
User-Agent: Tester By Heihu577
Referer: localhost?
Host: 127.0.0.1:2333
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
参数设置

原来代码的基础上, 拼接上参数即可:

// 准备参数, 并使用 URLEncode 进行一层编码
String password = URLEncoder.encode( "1 2+3|A_bc""UTF-8");
// 准备要请求的 URL
String url = "http://127.0.0.1:2333/login?username=Heihu577&password=" + password;

收到请求:

GET /login?username=Heihu577&password=1+2%2B3%7CA_bc HTTP/1.1
User-Agent: Tester By Heihu577
Referer: localhost?
Host: 127.0.0.1:2333
Connection: Keep-Alive
Accept-Encoding: gzip,deflate
获取响应头

CloseableHttpResponse.getStatusLine().getStatusCode()方法的调用以及CloseableHttpResponse.getHeaders()的调用.

// 准备参数, 并使用 URLEncode 进行一层编码
String password = URLEncoder.encode("1 2+3|A_bc""UTF-8");
// 准备要请求的 URL
String url = "http://127.0.0.1:2333/login?username=Heihu577&password=" + password;
// 创建可关闭的 httpClient 客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
// 准备 GET 请求对象
HttpGet httpGet = new HttpGet(url);
// 增加请求头
httpGet.addHeader("User-Agent""Tester By Heihu577");
httpGet.addHeader("Referer""localhost?");
// 准备响应对象
CloseableHttpResponse response = httpClient.execute(httpGet);
// 判断响应头是不是 200
if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
    // 得到服务端返回的响应头
    Header[] allHeaders = response.getAllHeaders();
    for (Header header : allHeaders) {
        System.out.println(header.getName() + ":" + header.getValue());
    }
    // 得到响应结果
    HttpEntity entity = response.getEntity();
    String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
    // 打印响应结果
    System.out.println(result); // HelloWorld!
    // 关闭流
    EntityUtils.consume(entity);
}
// 关闭结果
response.close();
// 关闭客户端
httpClient.close();

判断响应头, 获取响应头等信息.

保存响应体 (存储二进制值)

准备一张知乎的图片, 然后保存下来, 主要是对EntityUtils.toByteArrayAPI的调用:

CloseableHttpClient httpClient = HttpClients.createDefault();
String url = "https://pic4.zhimg.com/v2-267217db1a16cf81a50184ea7d520289_1440w.jpg";
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
byte[] byteArray = EntityUtils.toByteArray(entity); // 调用 API
new FileOutputStream("./img.jpg").write(byteArray); // 写入到 ./img.jpg
EntityUtils.consume(entity);
httpClient.close();
response.close();
设置访问代理 & 链接超时 & 读取超时

核心代码:

RequestConfig.Builder custom = RequestConfig.custom();
custom.setProxy(new HttpHost("127.0.0.1"8080)); // 可以设置成 BurpSuite 的代理
custom.setConnectTimeout(5000); // 连接超时, 单位毫秒, 5秒链接超时则报错
custom.setSocketTimeout(5000); // 读取超时, 5秒没有得到响应, 则抛出异常 (可以使用BP抓到包后不放包看到效果)
httpGet.setConfig(custom.build());

在这里可以使用Bp抓到代理结果:

开发 & 安全 | SSRF[URLConnection] 与 HttpClient

post 请求

参数 & Content-Type 设置

我们知道的是, POST 请求的 Content-Typeapplication/x-www-form-urlencoded, 所以在这里使用HttpClient发送该类型的请求:

String url = "http://127.0.0.1:2333/?id=1";
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url); // 使用 POST, 仅仅只是改变请求方式: POST / HTTP/1.1
List<NameValuePair> params = new Vector<>(); // 准备 POST 发送的主体参数
params.add(new BasicNameValuePair("name""heihu577")); // POST发送的值: name=heihu577
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(params); // 修改 ContentType: application/x-www-form-urlencoded
httpPost.setEntity(urlEncodedFormEntity); // 设置 Entity
CloseableHttpResponse execute = httpClient.execute(httpPost);
HttpEntity entity = execute.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
System.out.println(result);
httpClient.close();

nc 可以收到如下请求:

POST /?id=1 HTTP/1.1
Content-Length: 13
Content-Type: application/x-www-form-urlencoded
Host: 127.0.0.1:2333
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_131)
Accept-Encoding: gzip,deflate

name=heihu577

当然也可以通过HttpPost::addHeader进行设置, 就不再掩饰了.

json 类型请求

json 的 Content-Type 为 application/json, 这里不能再使用HttpClient提供的Entity了, 使用提供的原生的StringEntity.

首先我们需要引入json依赖, 这里使用 jackson:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.8</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.8</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>

进行编码, 发送该类型请求:

String url = "http://127.0.0.1:2333/?id=1";
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url); // 使用 POST, 仅仅只是改变请求方式: POST / HTTP/1.1
ObjectMapper objectMapper = new ObjectMapper(); // jackson 所需要的引用
HashMap<Object, Object> hsmap = new HashMap<>(); // 通过 HashMap 来生成一个 Json 串
hsmap.put("name""heihu577");
hsmap.put("age"12);
String jsonText = objectMapper.writeValueAsString(hsmap); // {"name":"heihu577","age":12}
StringEntity stringEntity = new StringEntity(jsonText, StandardCharsets.UTF_8); // 设置 POST 中的值
stringEntity.setContentType("application/json"); // 手动设置 Content-Type
httpPost.setEntity(stringEntity); // 设置 Entity
CloseableHttpResponse execute = httpClient.execute(httpPost);
HttpEntity entity = execute.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
System.out.println(result);
httpClient.close();

最终 nc 收到如下请求:

POST /?id=1 HTTP/1.1
Content-Length: 28
Content-Type: application/json
Host: 127.0.0.1:2333
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_131)
Accept-Encoding: gzip,deflate

{"name":"heihu577","age":12}

上传请求

上传请求Content-Typemultipart/form-data, 以及它的请求体是庞大的. 那么下面我们来看一下如何构造上传表单, 首先引入所需的依赖:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.5.13</version>
</dependency>

随后编码:

String url = "http://localhost:808/testFile";
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url); // 使用 POST, 仅仅只是改变请求方式: POST / HTTP/1.1

MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); // 定义一个上传表单
multipartEntityBuilder.setCharset(StandardCharsets.UTF_8); // 设置编码为 UTF-8
multipartEntityBuilder.addTextBody("name""heihu577"). // 增加参数
addBinaryBody("file"new File("C:/Windows/win.ini"), ContentType.create("text/plain", Consts.UTF_8), "win.ini"). // 增加上传文件, 也可以不指定 content-type 以及文件名
addPart("file2"new StringBody("This is text, 简单文本~", ContentType.create("text/plain", Consts.UTF_8))); // 自定义一个上传流

multipartEntityBuilder.setContentType(ContentType.create("multipart/form-data", Consts.UTF_8)); // 设置为上传表单
multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); // 设置为浏览器上传模式
HttpEntity httpEntity = multipartEntityBuilder.build(); // 通过 MultipartEntityBuilder 来 build 一个 Entity

httpPost.setEntity(httpEntity); // 设置 Entity
CloseableHttpResponse execute = httpClient.execute(httpPost);
HttpEntity entity = execute.getEntity();
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
System.out.println(result);
httpClient.close();

最终 nc 收到的请求为:

POST /upfile HTTP/1.1
Content-Length: 510
Content-Type: multipart/form-data; boundary=aDHeP31NhDkZsg-jxq0TK7L7oL2FjcMN_w7KWrY; charset=UTF-8
Host: 127.0.0.1:2333
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_131)
Accept-Encoding: gzip,deflate

--aDHeP31NhDkZsg-jxq0TK7L7oL2FjcMN_w7KWrY
Content-Disposition: form-data; name="name"

heihu577
--aDHeP31NhDkZsg-jxq0TK7L7oL2FjcMN_w7KWrY
Content-Disposition: form-data; name="file"; filename="win.ini"
Content-Type: text/plain; charset=UTF-8

; for 16-bit app support
[fonts]
[extensions]
[mci extensions]
[files]
[Mail]
MAPI=1

--aDHeP31NhDkZsg-jxq0TK7L7oL2FjcMN_w7KWrY
Content-Disposition: form-data; name="file2"

This is text, 简单文本~
--aDHeP31NhDkZsg-jxq0TK7L7oL2FjcMN_w7KWrY--

其他常用功能

登陆携带cookie访问下一个连接

创建setcookie.php文件内容如下:

<?php
setcookie('name''heihu577');

创建getcookie.php文件内容如下:

<?php
echo isset($_COOKIE['name']) ? $_COOKIE['name'] : 'NoCookie';

若服务器最终得到的是heihu577, 则意味着cookie被成功配置到服务器端.

准备如下代码:

// 创建一个Cookie存储对象
CookieStore cookieStore = new BasicCookieStore();
// 创建一个支持Cookie的HttpClient
CloseableHttpClient httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
// 访问后 cookieStore中将保存服务器发送的Cookie, 接下来可以使用这些Cookie访问受保护的资源
httpClient.execute(new HttpGet("http://localhost/setcookie.php"));
// 访问 getcookie.php
CloseableHttpResponse response = httpClient.execute(new HttpGet("http://localhost/getcookie.php"));
HttpEntity entity = response.getEntity();
// 最终得到Cookie: heihu577
System.out.println(EntityUtils.toString(entity));
// 关闭一些资源...
EntityUtils.consume(entity);
response.close();
httpClient.close();

绕过 https 安全认证

在这里我们获取CloseableHttpClient不能再像往常一样这样获取:

CloseableHttpClient httpClient = HttpClients.createDefault();

而是改为如下获取方式:

CloseableHttpClient httpClient = createHttpClientWithNoSsl();

其中createHttpClientWithNoSsl方法定义如下:

public static CloseableHttpClient createHttpClientWithNoSsl() throws Exception {
    // Create a trust manager that does not validate certificate chains
    TrustManager[] trustAllCerts = new TrustManager[]{
            new X509TrustManager() {
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// don't check
}

@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// don't check
}
}
};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAllCerts, null);
LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(ctx);
return HttpClients.custom()
.setSSLSocketFactory(sslSocketFactory)
.build();
}

连接池配置

配置如下:

public class HttpUtilTest {
    public static CloseableHttpClient createClient() {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(100); // 最大连接数
        cm.setDefaultMaxPerRoute(20); // 每个路由的默认最大连接数

return HttpClients.custom()
.setConnectionManager(cm)
.build();
}

@Test
public void t1() {
long l = System.currentTimeMillis();
CloseableHttpClient client = createClient(); // 可以观察到与 HttpClients.createDefault() 的区别
for (int i = 0; i < 20; i++) {
try {
HttpGet get = new HttpGet("http://www.baidu.com");
CloseableHttpResponse response = client.execute(get);
System.out.println(response.getStatusLine());
catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("耗时:" + (System.currentTimeMillis() - l));
}
}

连接池 + https 绕过安全认证

在这里如果往常配置会报错, 参考: https://blog.csdn.net/u013310119/article/details/81906571 给出解决方案, 在构造方法中进行配置即可, 最终CloseableHttpClient初始化代码如下:

public static CloseableHttpClient createClient() throws KeyManagementException, NoSuchAlgorithmException {
    TrustManager[] trustAllCerts = new TrustManager[]{
            new X509TrustManager() {
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                @Override
                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    // don't check
                }
                @Override
                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    // don't check
                }
            }
    };
    SSLContext ctx = SSLContext.getInstance("TLS");
    ctx.init(null, trustAllCerts, null);
    LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(ctx);
    PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", PlainConnectionSocketFactory.getSocketFactory())
            .register("https", sslSocketFactory).build());
    cm.setMaxTotal(100); // 最大连接数
    cm.setDefaultMaxPerRoute(20); // 每个路由的默认最大连接数
    return HttpClients.custom()
            .setConnectionManager(cm)
            .build();
}

Ending...

https://www.jb51.net/article/240169.htm

https://www.cnblogs.com/wgslucky/p/11270038.html

https://isenninha.github.io/2020/11/12/http-client/

https://blog.csdn.net/u013310119/article/details/81906571

 

原文始发于微信公众号(Heihu Share):开发 & 安全 | SSRF[URLConnection] 与 HttpClient

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月28日00:35:05
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   开发 & 安全 | SSRF[URLConnection] 与 HttpClienthttp://cn-sec.com/archives/3442913.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息