mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-08-30 05:22:01 +00:00
fix GHSA-997r-7xx2-p9x6
This commit is contained in:
@@ -20,6 +20,9 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -201,6 +204,76 @@ public abstract class PanBase implements IPanTool, Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSRF 防护: 校验通用自定义域名解析器 (CE/Ce4/Kd/Other 等) 即将请求的目标主机,
|
||||||
|
* 拒绝解析到回环/内网/链路本地/组播等非公网地址的域名, 阻止攻击者通过可控 DNS
|
||||||
|
* 记录 (或直接填写内网域名) 让服务端向内网/云元数据接口发起请求。
|
||||||
|
* <p>
|
||||||
|
* 必须在子类 parse() 中构造出 baseUrl/发起任何 clientSession 请求之前调用。
|
||||||
|
*
|
||||||
|
* @param url 从 shareLinkInfo.getShareUrl() 解析出的 URL
|
||||||
|
* @throws IOException 当主机无法解析或解析结果落入禁止的地址段时抛出
|
||||||
|
*/
|
||||||
|
protected static void assertPublicHost(URL url) throws IOException {
|
||||||
|
String host = url.getHost();
|
||||||
|
InetAddress[] addresses;
|
||||||
|
try {
|
||||||
|
addresses = InetAddress.getAllByName(host);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
throw new IOException("无法解析目标主机: " + host, e);
|
||||||
|
}
|
||||||
|
if (addresses.length == 0) {
|
||||||
|
throw new IOException("无法解析目标主机: " + host);
|
||||||
|
}
|
||||||
|
for (InetAddress addr : addresses) {
|
||||||
|
if (isDisallowedAddress(addr)) {
|
||||||
|
throw new IOException("目标地址不允许访问(内网/回环/链路本地/组播): "
|
||||||
|
+ host + " -> " + addr.getHostAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断地址是否落入禁止访问的范围: 0.0.0.0/8, 10/8, 127/8, 169.254/16(含云元数据
|
||||||
|
* 169.254.169.254), 172.16/12, 192.168/16, 100.64/10(CGNAT), 224/4~255/4(组播/保留),
|
||||||
|
* 以及对应的 IPv6 回环/链路本地/唯一本地地址(fc00::/7)/组播地址; IPv4-映射的 IPv6
|
||||||
|
* 地址(::ffff:a.b.c.d) 会先还原为 IPv4 再判断,避免绕过。
|
||||||
|
*/
|
||||||
|
private static boolean isDisallowedAddress(InetAddress addr) {
|
||||||
|
byte[] bytes = addr.getAddress();
|
||||||
|
if (bytes.length == 16 && isIPv4Mapped(bytes)) {
|
||||||
|
byte[] v4 = new byte[4];
|
||||||
|
System.arraycopy(bytes, 12, v4, 0, 4);
|
||||||
|
bytes = v4;
|
||||||
|
}
|
||||||
|
if (bytes.length == 4) {
|
||||||
|
int b0 = bytes[0] & 0xFF;
|
||||||
|
int b1 = bytes[1] & 0xFF;
|
||||||
|
if (b0 == 0) return true; // 0.0.0.0/8
|
||||||
|
if (b0 == 10) return true; // 10.0.0.0/8
|
||||||
|
if (b0 == 127) return true; // 127.0.0.0/8 loopback
|
||||||
|
if (b0 == 169 && b1 == 254) return true; // 169.254.0.0/16 (含云元数据 169.254.169.254)
|
||||||
|
if (b0 == 172 && b1 >= 16 && b1 <= 31) return true; // 172.16.0.0/12
|
||||||
|
if (b0 == 192 && b1 == 168) return true; // 192.168.0.0/16
|
||||||
|
if (b0 == 100 && b1 >= 64 && b1 <= 127) return true;// 100.64.0.0/10 CGNAT
|
||||||
|
return b0 >= 224; // 224.0.0.0/4 组播 + 240.0.0.0/4 保留
|
||||||
|
}
|
||||||
|
// IPv6
|
||||||
|
if (addr.isAnyLocalAddress() || addr.isLoopbackAddress()
|
||||||
|
|| addr.isLinkLocalAddress() || addr.isSiteLocalAddress()
|
||||||
|
|| addr.isMulticastAddress()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return bytes.length == 16 && (bytes[0] & 0xFE) == 0xFC; // fc00::/7 unique local
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isIPv4Mapped(byte[] b) {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (b[i] != 0) return false;
|
||||||
|
}
|
||||||
|
return (b[10] & 0xFF) == 0xFF && (b[11] & 0xFF) == 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
protected String baseMsg() {
|
protected String baseMsg() {
|
||||||
if (shareLinkInfo.getShareUrl() != null) {
|
if (shareLinkInfo.getShareUrl() != null) {
|
||||||
return shareLinkInfo.getPanName() + "-" + shareLinkInfo.getType() + ": url=" + shareLinkInfo.getShareUrl();
|
return shareLinkInfo.getPanName() + "-" + shareLinkInfo.getType() + ": url=" + shareLinkInfo.getShareUrl();
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public class Ce4Tool extends PanBase {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
URL url = new URL(shareLinkInfo.getShareUrl());
|
URL url = new URL(shareLinkInfo.getShareUrl());
|
||||||
|
assertPublicHost(url);
|
||||||
String baseUrl = url.getProtocol() + "://" + url.getHost();
|
String baseUrl = url.getProtocol() + "://" + url.getHost();
|
||||||
// 如果有端口,拼接上端口
|
// 如果有端口,拼接上端口
|
||||||
if (url.getPort() != -1) {
|
if (url.getPort() != -1) {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public class CeTool extends PanBase {
|
|||||||
String pwd = shareLinkInfo.getSharePassword();
|
String pwd = shareLinkInfo.getSharePassword();
|
||||||
try {
|
try {
|
||||||
URL url = new URL(shareLinkInfo.getShareUrl());
|
URL url = new URL(shareLinkInfo.getShareUrl());
|
||||||
|
assertPublicHost(url);
|
||||||
String baseUrl = url.getProtocol() + "://" + url.getHost();
|
String baseUrl = url.getProtocol() + "://" + url.getHost();
|
||||||
// 如果有端口,拼接上端口
|
// 如果有端口,拼接上端口
|
||||||
if (url.getPort() != -1) {
|
if (url.getPort() != -1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user