From 17460ff2719fd89e9efc9eb72b165db7a237f271 Mon Sep 17 00:00:00 2001 From: yukaidi Date: Fri, 29 May 2026 14:20:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(security):=20=E5=AE=89=E5=85=A8=E6=BC=8F?= =?UTF-8?q?=E6=B4=9E=E4=BF=AE=E5=A4=8D=E4=B8=8E=E4=BE=9D=E8=B5=96=E5=8D=87?= =?UTF-8?q?=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 升级 Vert.x 4.5.24 → 4.5.27, postgresql 42.7.3 → 42.7.11, logback 1.5.18 → 1.5.32, axios 1.13.5 → 1.16.1 - 修复 JWT 签名验证和密码比较的时序攻击漏洞 (MessageDigest.isEqual) - 修复 AESUtils 使用不安全 Random 改为 SecureRandom - 修复登录用户枚举和异常信息泄露,统一错误提示 - 修复 RateLimiter count++ 非原子操作 (AtomicInteger) - 修复 JsParserExecutor DCL 模式缺少 volatile - 修复 Token 日志泄露,仅打印前8字符 - 修复 Playground 密码时序攻击和堆栈泄露 - 所有 window.open 添加 noopener,noreferrer - LocalConstant 改用 ConcurrentHashMap 保证线程安全 - Dockerfile 添加非 root 用户运行,secret.yml 加入 .gitignore --- .gitignore | 1 + Dockerfile | 11 +- core-database/pom.xml | 2 +- .../handlerfactory/RouterHandlerFactory.java | 35 +++--- .../cn/qaiu/vx/core/util/LocalConstant.java | 7 +- docker-entrypoint.sh | 9 ++ parser/pom.xml | 48 ++++++-- .../parser/customjs/JsParserExecutor.java | 2 +- .../src/main/java/cn/qaiu/util/AESUtils.java | 3 +- pom.xml | 9 +- web-front/package.json | 6 +- web-front/src/components/DarkMode.vue | 1 - web-front/src/views/ClientLinks.vue | 29 +---- web-front/src/views/Home.vue | 104 +++++++++++++----- web-front/src/views/Playground.vue | 23 ++-- web-front/src/views/ShowFile.vue | 6 +- web-front/src/views/ShowList.vue | 2 +- .../common/interceptorImpl/RateLimiter.java | 9 +- .../java/cn/qaiu/lz/common/util/JwtUtil.java | 6 +- .../cn/qaiu/lz/common/util/PasswordUtil.java | 6 +- .../qaiu/lz/web/controller/PlaygroundApi.java | 28 ++--- .../lz/web/service/impl/UserServiceImpl.java | 20 ++-- 22 files changed, 212 insertions(+), 155 deletions(-) create mode 100644 docker-entrypoint.sh diff --git a/.gitignore b/.gitignore index 32c8707..6cfc6c6 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ target/ sdkTest.log app.yml app-local.yml +secret.yml #some local files diff --git a/Dockerfile b/Dockerfile index 8d50655..be3c89b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,8 +10,13 @@ COPY ./web-service/target/netdisk-fast-download-bin.zip . RUN unzip netdisk-fast-download-bin.zip && \ mv netdisk-fast-download/* ./ && \ rm netdisk-fast-download-bin.zip && \ - chmod +x run.sh + chmod +x run.sh && \ + mkdir -p db logs -EXPOSE 6400 6401 +COPY ./docker-entrypoint.sh /docker-entrypoint.sh +RUN chmod +x /docker-entrypoint.sh -ENTRYPOINT ["sh", "run.sh"] +EXPOSE 6401 + +RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser +ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/core-database/pom.xml b/core-database/pom.xml index 398fd2d..0ae8493 100644 --- a/core-database/pom.xml +++ b/core-database/pom.xml @@ -65,7 +65,7 @@ org.postgresql postgresql - 42.7.3 + 42.7.11 diff --git a/core/src/main/java/cn/qaiu/vx/core/handlerfactory/RouterHandlerFactory.java b/core/src/main/java/cn/qaiu/vx/core/handlerfactory/RouterHandlerFactory.java index aca7f7b..92bb224 100644 --- a/core/src/main/java/cn/qaiu/vx/core/handlerfactory/RouterHandlerFactory.java +++ b/core/src/main/java/cn/qaiu/vx/core/handlerfactory/RouterHandlerFactory.java @@ -127,8 +127,9 @@ public class RouterHandlerFactory implements BaseHttpApi { // 错误请求处理 mainRouter.errorHandler(405, ctx -> doFireJsonResultResponse(ctx, JsonResult .error("Method Not Allowed", 405))); - mainRouter.errorHandler(404, ctx -> ctx.response().setStatusCode(404).setChunked(true) - .end("Internal server error: 404 not found")); + mainRouter.errorHandler(404, ctx -> { + ctx.response().setStatusCode(404).end("404 not found"); + }); return mainRouter; } @@ -179,8 +180,9 @@ public class RouterHandlerFactory implements BaseHttpApi { if (ctx.statusCode() == 503 || ctx.failure() == null) { doFireJsonResultResponse(ctx, JsonResult.error("未知异常, 请联系管理员"), 503); } else { - ctx.failure().printStackTrace(); - doFireJsonResultResponse(ctx, JsonResult.error(ctx.failure().getMessage()), 500); + LOGGER.error("路由处理失败", ctx.failure()); + String msg = ctx.failure() != null ? ctx.failure().getMessage() : "未知异常"; + doFireJsonResultResponse(ctx, JsonResult.error(msg), 500); } }); } else if (method.isAnnotationPresent(SockRouteMapper.class)) { @@ -198,7 +200,7 @@ public class RouterHandlerFactory implements BaseHttpApi { try { ReflectionUtil.invokeWithArguments(method, instance, sock); } catch (Throwable e) { - e.printStackTrace(); + LOGGER.error("WebSocket处理异常", e); } }); if (url.endsWith("*")) { @@ -322,7 +324,7 @@ public class RouterHandlerFactory implements BaseHttpApi { parameterValueList.put(k, entity); } } catch (ClassNotFoundException e) { - e.printStackTrace(); + LOGGER.error("实体类绑定异常: {}", typeName, e); } } }); @@ -365,7 +367,7 @@ public class RouterHandlerFactory implements BaseHttpApi { Object entity = ParamUtil.multiMapToEntity(queryParams, aClass); parameterValueList.put(k, entity); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error("参数绑定异常: {}", v.getRight().getName(), e); } } else if (parameterValueList.get(k) == null && JsonObject.class.getName().equals(v.getRight().getName())) { @@ -408,22 +410,19 @@ public class RouterHandlerFactory implements BaseHttpApi { doFireJsonResultResponse(ctx, JsonResult.data(null)); } - }).onFailure(e -> doFireJsonResultResponse(ctx, JsonResult.error(e.getMessage()), 500)); + }).onFailure(e -> { + LOGGER.error("请求处理失败", e); + String msg = e.getMessage() != null ? e.getMessage() : "服务器内部错误"; + doFireJsonResultResponse(ctx, JsonResult.error(msg), 500); + }); } else { doFireJsonResultResponse(ctx, JsonResult.data(data)); } } } catch (Throwable e) { - e.printStackTrace(); - String err = e.getMessage(); - if (e.getCause() != null) { - if (e.getCause() instanceof InvocationTargetException) { - err = ((InvocationTargetException) e.getCause()).getTargetException().getMessage(); - } else { - err = e.getCause().getMessage(); - } - } - doFireJsonResultResponse(ctx, JsonResult.error(err), 500); + LOGGER.error("请求处理异常", e); + String msg = e.getMessage() != null ? e.getMessage() : "服务器内部错误"; + doFireJsonResultResponse(ctx, JsonResult.error(msg), 500); } } diff --git a/core/src/main/java/cn/qaiu/vx/core/util/LocalConstant.java b/core/src/main/java/cn/qaiu/vx/core/util/LocalConstant.java index 0a7fa8c..6111e9b 100644 --- a/core/src/main/java/cn/qaiu/vx/core/util/LocalConstant.java +++ b/core/src/main/java/cn/qaiu/vx/core/util/LocalConstant.java @@ -1,7 +1,7 @@ package cn.qaiu.vx.core.util; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * vertx 上下文外的本地容器 为不在vertx线程的方法传递数据 @@ -10,11 +10,10 @@ import java.util.Map; * @author QAIU */ public class LocalConstant { - private static final Map LOCAL_CONST = new HashMap<>(); + private static final Map LOCAL_CONST = new ConcurrentHashMap<>(); public static Map put(String k, Object v) { - if (LOCAL_CONST.containsKey(k)) return LOCAL_CONST; - LOCAL_CONST.put(k, v); + LOCAL_CONST.putIfAbsent(k, v); return LOCAL_CONST; } diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..2bc18db --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -e + +# Fix permissions on volume-mounted directories (runs as root) +chown -R appuser:appgroup /app/db /app/logs /app/resources 2>/dev/null || true + +# Run Java directly - entrypoint is PID 1, exec makes Java PID 1 +# Docker SIGTERM goes directly to Java, triggering ShutdownHook +exec java -Xmx${JVM_XMX:-512M} ${JVM_OPTS} -Duser.timezone=${TZ:-Asia/Shanghai} -jar /app/netdisk-fast-download.jar diff --git a/parser/pom.xml b/parser/pom.xml index b5791d1..2a55f4d 100644 --- a/parser/pom.xml +++ b/parser/pom.xml @@ -12,7 +12,7 @@ cn.qaiu parser - 10.2.5 + ${parserVersion} jar cn.qaiu:parser @@ -35,9 +35,9 @@ - scm:git:https://github.com/qaiu/netdisk-fast-download.git - scm:git:ssh://git@github.com:qaiu/netdisk-fast-download.git - https://github.com/qaiu/netdisk-fast-download + scm:git:https://github.com/${github.owner}/${github.repo}.git + scm:git:ssh://git@github.com:${github.owner}/${github.repo}.git + https://github.com/${github.owner}/${github.repo} @@ -52,20 +52,19 @@ - 0.2.1 17 17 17 UTF-8 - 4.5.24 + 4.5.27 0.10.2 1.18.38 2.0.16 3.18.0 2.18.6 - 1.5.19 + 1.5.32 4.13.2 @@ -124,6 +123,41 @@ + + + org.codehaus.gmavenplus + gmavenplus-plugin + 4.1.1 + + + org.apache.groovy + groovy + 4.0.24 + + + + + initialize + execute + + + + + + + + + org.apache.maven.plugins diff --git a/parser/src/main/java/cn/qaiu/parser/customjs/JsParserExecutor.java b/parser/src/main/java/cn/qaiu/parser/customjs/JsParserExecutor.java index 69310b4..7226e0b 100644 --- a/parser/src/main/java/cn/qaiu/parser/customjs/JsParserExecutor.java +++ b/parser/src/main/java/cn/qaiu/parser/customjs/JsParserExecutor.java @@ -33,7 +33,7 @@ public class JsParserExecutor implements IPanTool, AutoCloseable { private static final Logger log = LoggerFactory.getLogger(JsParserExecutor.class); - private static WorkerExecutor EXECUTOR; + private static volatile WorkerExecutor EXECUTOR; private static final Object EXECUTOR_LOCK = new Object(); private static String FETCH_RUNTIME_JS = null; diff --git a/parser/src/main/java/cn/qaiu/util/AESUtils.java b/parser/src/main/java/cn/qaiu/util/AESUtils.java index 2fc41c7..a3a42b0 100644 --- a/parser/src/main/java/cn/qaiu/util/AESUtils.java +++ b/parser/src/main/java/cn/qaiu/util/AESUtils.java @@ -14,7 +14,6 @@ import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.Date; import java.util.HexFormat; -import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -299,7 +298,7 @@ public class AESUtils { //length用户要求产生字符串的长度 public static String getRandomString(int length){ String str="abcdefghijklmnopqrstuvwxyz0123456789"; - Random random=new Random(); + SecureRandom random=new SecureRandom(); StringBuilder sb=new StringBuilder(); for(int i=0;i${project.basedir}/web-service/target/package - - 4.5.24 + + 4.5.27 0.10.2 1.18.38 2.0.16 3.18.0 2.0.0 + 10.2.5 2.18.6 - 1.5.18 + 1.5.32 4.13.2 @@ -74,7 +75,7 @@ cn.qaiu parser - 10.2.5 + ${parserVersion} diff --git a/web-front/package.json b/web-front/package.json index b73baba..5abfb36 100644 --- a/web-front/package.json +++ b/web-front/package.json @@ -5,15 +5,15 @@ "scripts": { "serve": "vue-cli-service serve", "dev": "vue-cli-service serve", - "build": "vue-cli-service build && node scripts/compress-vs.js", - "build:no-compress": "vue-cli-service build", + "build": "node scripts/sync-version.js && vue-cli-service build && node scripts/compress-vs.js", + "build:no-compress": "node scripts/sync-version.js && vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "@element-plus/icons-vue": "^2.3.1", "@monaco-editor/loader": "^1.4.0", "@vueuse/core": "^11.2.0", - "axios": "1.13.5", + "axios": "1.16.1", "clipboard": "^2.0.11", "core-js": "^3.8.3", "crypto-js": "^4.2.0", diff --git a/web-front/src/components/DarkMode.vue b/web-front/src/components/DarkMode.vue index b41e4ea..5a3870f 100644 --- a/web-front/src/components/DarkMode.vue +++ b/web-front/src/components/DarkMode.vue @@ -36,7 +36,6 @@ if (item) { const darkMode = ref(item) watch(darkMode, (newValue) => { - console.log(`darkMode: ${newValue}`) window.localStorage.setItem("darkMode", newValue); // 发射主题变化事件 diff --git a/web-front/src/views/ClientLinks.vue b/web-front/src/views/ClientLinks.vue index 0080a6b..ecb6bed 100644 --- a/web-front/src/views/ClientLinks.vue +++ b/web-front/src/views/ClientLinks.vue @@ -293,24 +293,6 @@ export default { return clientConfig[type]?.downloadUrl || '#' } - // 判断是否应该显示下载客户端按钮 - const shouldShowDownloadButton = (type) => { - const os = getOSInfo() - switch (type) { - case 'CURL': - // cURL 在 Windows 上可能需要安装 - return os === 'windows' - case 'ARIA2': - // Aria2 需要手动安装 - return true - case 'THUNDER': - // 迅雷主要在 Windows 上使用 - return os === 'windows' - default: - return false - } - } - // 获取操作系统信息 const getOSInfo = () => { const userAgent = navigator.userAgent.toLowerCase() @@ -369,7 +351,7 @@ export default { copyToClipboard(link) return } - window.open(link, '_blank') + window.open(link, '_blank', 'noopener,noreferrer') ElMessage.success('正在唤起迅雷下载') break @@ -383,13 +365,6 @@ export default { } } - // 下载客户端 - const downloadClient = (type) => { - const url = getClientDownloadUrl(type) - window.open(url, '_blank') - ElMessage.success(`正在跳转到 ${getClientDisplayName(type)} 下载页面`) - } - // 格式化文件大小 const formatFileSize = (bytes) => { if (!bytes) return '未知' @@ -440,9 +415,7 @@ export default { getTextareaRows, goBack, getClientLogo, - downloadClient, handleImageError, - shouldShowDownloadButton, getClientSupportsCookie, goToAuthConfig } diff --git a/web-front/src/views/Home.vue b/web-front/src/views/Home.vue index 6e9f719..8456887 100644 --- a/web-front/src/views/Home.vue +++ b/web-front/src/views/Home.vue @@ -19,11 +19,11 @@ -->
-
NFD网盘直链解析0.3.0
+
NFD网盘直链解析 {{ projectVersion }}
-
支持网盘:蓝奏云、蓝奏云优享、小飞机盘、123云盘、iCloud、移动云空间、联想乐云、QQ闪传等 >>
+
支持网盘:蓝奏云、蓝奏云优享、小飞机盘、123云盘、iCloud、移动云空间、联想乐云、QQ闪传等 >>
文件夹解析支持:蓝奏云、蓝奏云优享、小飞机盘、123云盘
@@ -90,7 +90,7 @@ - +