mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-08-30 05:22:01 +00:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a5240fcbc | ||
|
|
ecd365767a | ||
|
|
f358140974 | ||
|
|
541c21f963 | ||
|
|
bdd253383d | ||
|
|
0fb53d5159 | ||
|
|
b9ff408bdd | ||
|
|
aeb4394cf8 | ||
|
|
8d419d3265 | ||
|
|
363c603bbb |
@@ -531,7 +531,13 @@ Core模块集成Vert.x实现类似spring的注解式路由API
|
|||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
[](https://star-history.com/#qaiu/netdisk-fast-download&Date)
|
<a href="https://www.star-history.com/?repos=qaiu%2Fnetdisk-fast-download&type=date&legend=bottom-right">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=qaiu/netdisk-fast-download&type=date&theme=dark&legend=bottom-right&sealed_token=dfQO_dJcTqcPkEnM7SfxRyHoFbV5Ah4LxoEhdlheMn4T2YLEV_WETxFZexeAbWN5OmNyYuycWan2d42PAFbw0CuU4oCTKgehfErFJ9eVl2CyVpP_4xrdQw" />
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=qaiu/netdisk-fast-download&type=date&legend=bottom-right&sealed_token=dfQO_dJcTqcPkEnM7SfxRyHoFbV5Ah4LxoEhdlheMn4T2YLEV_WETxFZexeAbWN5OmNyYuycWan2d42PAFbw0CuU4oCTKgehfErFJ9eVl2CyVpP_4xrdQw" />
|
||||||
|
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=qaiu/netdisk-fast-download&type=date&legend=bottom-right&sealed_token=dfQO_dJcTqcPkEnM7SfxRyHoFbV5Ah4LxoEhdlheMn4T2YLEV_WETxFZexeAbWN5OmNyYuycWan2d42PAFbw0CuU4oCTKgehfErFJ9eVl2CyVpP_4xrdQw" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
|
||||||
## **免责声明**
|
## **免责声明**
|
||||||
- 用户在使用本项目时,应自行承担风险,并确保其行为符合当地法律法规。开发者不对用户因使用本项目而导致的任何后果负责。
|
- 用户在使用本项目时,应自行承担风险,并确保其行为符合当地法律法规。开发者不对用户因使用本项目而导致的任何后果负责。
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.postgresql</groupId>
|
<groupId>org.postgresql</groupId>
|
||||||
<artifactId>postgresql</artifactId>
|
<artifactId>postgresql</artifactId>
|
||||||
<version>42.7.11</version>
|
<version>42.7.12</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
<parserVersion>10.2.5</parserVersion>
|
<parserVersion>10.2.5</parserVersion>
|
||||||
<jackson.version>2.18.6</jackson.version>
|
<jackson.version>2.18.6</jackson.version>
|
||||||
<!-- Logback 最新稳定版 -->
|
<!-- Logback 最新稳定版 -->
|
||||||
<logback.version>1.5.32</logback.version>
|
<logback.version>1.5.33</logback.version>
|
||||||
<junit.version>4.13.2</junit.version>
|
<junit.version>4.13.2</junit.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"@monaco-editor/loader": "^1.4.0",
|
"@monaco-editor/loader": "^1.4.0",
|
||||||
"@vueuse/core": "^11.2.0",
|
"@vueuse/core": "^11.2.0",
|
||||||
"axios": "1.16.1",
|
"axios": "1.18.0",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
|
|||||||
@@ -386,6 +386,16 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "uuid123"
|
"example": "uuid123"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stoken",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"description": "分享 token,用于子目录解析时复用认证状态",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "OASBe5qM0pg2VvvyLM..."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|||||||
@@ -68,9 +68,9 @@ public class URLParamUtil {
|
|||||||
boolean firstParam = !decodedUrl.contains("?");
|
boolean firstParam = !decodedUrl.contains("?");
|
||||||
|
|
||||||
for (String paramName : params.names()) {
|
for (String paramName : params.names()) {
|
||||||
// 忽略 "url", "pwd", "dirId", "uuid", "auth" 参数(这些参数单独处理,不应拼接到分享URL中)
|
// 忽略 "url", "pwd", "dirId", "uuid", "auth", "stoken" 参数(这些参数单独处理,不应拼接到分享URL中)
|
||||||
if (!paramName.equals("url") && !paramName.equals("pwd") && !paramName.equals("dirId")
|
if (!paramName.equals("url") && !paramName.equals("pwd") && !paramName.equals("dirId")
|
||||||
&& !paramName.equals("uuid") && !paramName.equals("auth")) {
|
&& !paramName.equals("uuid") && !paramName.equals("auth") && !paramName.equals("stoken")) {
|
||||||
if (firstParam) {
|
if (firstParam) {
|
||||||
urlBuilder.append("?");
|
urlBuilder.append("?");
|
||||||
firstParam = false;
|
firstParam = false;
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ public class ParserApi {
|
|||||||
|
|
||||||
@RouteMapping("/getFileList")
|
@RouteMapping("/getFileList")
|
||||||
public Future<List<FileInfo>> getFileList(HttpServerRequest request, String pwd, String dirId, String uuid,
|
public Future<List<FileInfo>> getFileList(HttpServerRequest request, String pwd, String dirId, String uuid,
|
||||||
String auth) {
|
String stoken, String auth) {
|
||||||
String url = URLParamUtil.parserParams(request);
|
String url = URLParamUtil.parserParams(request);
|
||||||
ParserCreate parserCreate;
|
ParserCreate parserCreate;
|
||||||
try {
|
try {
|
||||||
@@ -176,6 +176,9 @@ public class ParserApi {
|
|||||||
if (StringUtils.isNotBlank(dirId)) {
|
if (StringUtils.isNotBlank(dirId)) {
|
||||||
parserCreate.getShareLinkInfo().getOtherParam().put("dirId", dirId);
|
parserCreate.getShareLinkInfo().getOtherParam().put("dirId", dirId);
|
||||||
}
|
}
|
||||||
|
if (StringUtils.isNotBlank(stoken)) {
|
||||||
|
parserCreate.getShareLinkInfo().getOtherParam().put("stoken", stoken);
|
||||||
|
}
|
||||||
if (StringUtils.isNotBlank(uuid)) {
|
if (StringUtils.isNotBlank(uuid)) {
|
||||||
parserCreate.getShareLinkInfo().getOtherParam().put("uuid", uuid);
|
parserCreate.getShareLinkInfo().getOtherParam().put("uuid", uuid);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user