mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-06-11 16:07:27 +00:00
fix(security): 安全漏洞修复与依赖升级
- 升级 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
This commit is contained in:
@@ -10,6 +10,7 @@ import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Slf4j
|
||||
public class RateLimiter {
|
||||
@@ -51,12 +52,12 @@ public class RateLimiter {
|
||||
return new RequestInfo(1, currentTime);
|
||||
} else {
|
||||
// 增加计数器
|
||||
requestInfo.count++;
|
||||
requestInfo.count.incrementAndGet();
|
||||
return requestInfo;
|
||||
}
|
||||
});
|
||||
|
||||
if (info.count > MAX_REQUESTS) {
|
||||
if (info.count.get() > MAX_REQUESTS) {
|
||||
// 超过限制
|
||||
// 计算剩余时间
|
||||
long remainingTime = TIME_WINDOW - (System.currentTimeMillis() - info.timestamp);
|
||||
@@ -71,11 +72,11 @@ public class RateLimiter {
|
||||
}
|
||||
|
||||
private static class RequestInfo {
|
||||
volatile int count;
|
||||
final AtomicInteger count;
|
||||
volatile long timestamp;
|
||||
|
||||
RequestInfo(int count, long time) {
|
||||
this.count = count;
|
||||
this.count = new AtomicInteger(count);
|
||||
this.timestamp = time;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -93,9 +94,10 @@ public class JwtUtil {
|
||||
String encodedPayload = parts[1];
|
||||
String signature = parts[2];
|
||||
|
||||
// 验证签名
|
||||
// 验证签名(使用常量时间比较防止时序攻击)
|
||||
String expectedSignature = hmacSha256(encodedHeader + "." + encodedPayload, SECRET_KEY);
|
||||
if (!expectedSignature.equals(signature)) {
|
||||
if (!MessageDigest.isEqual(expectedSignature.getBytes(StandardCharsets.UTF_8),
|
||||
signature.getBytes(StandardCharsets.UTF_8))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,8 +80,10 @@ public class PasswordUtil {
|
||||
byte[] calculatedHash = md.digest(plainPassword.getBytes(StandardCharsets.UTF_8));
|
||||
String calculatedHashBase64 = Base64.getEncoder().encodeToString(calculatedHash);
|
||||
|
||||
// 比较计算出的哈希值和存储的哈希值
|
||||
return hashBase64.equals(calculatedHashBase64);
|
||||
// 比较计算出的哈希值和存储的哈希值(使用常量时间比较防止时序攻击)
|
||||
return MessageDigest.isEqual(
|
||||
hashBase64.getBytes(StandardCharsets.UTF_8),
|
||||
calculatedHashBase64.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (Exception e) {
|
||||
// 如果发生异常(例如格式不正确),返回false
|
||||
return false;
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -129,8 +130,11 @@ public class PlaygroundApi {
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (config.getPassword().equals(password)) {
|
||||
// 验证密码(使用常量时间比较防止时序攻击)
|
||||
String storedPassword = config.getPassword();
|
||||
if (storedPassword != null && MessageDigest.isEqual(
|
||||
storedPassword.getBytes(StandardCharsets.UTF_8),
|
||||
password.getBytes(StandardCharsets.UTF_8))) {
|
||||
String token = config.generateToken();
|
||||
JsonObject tokenData = new JsonObject().put("token", token);
|
||||
promise.complete(JsonResult.data(tokenData).toJsonObject());
|
||||
@@ -299,7 +303,6 @@ public class PlaygroundApi {
|
||||
}).onFailure(e -> {
|
||||
long executionTime = System.currentTimeMillis() - startTime;
|
||||
String errorMessage = e.getMessage();
|
||||
String stackTrace = getStackTrace(e);
|
||||
|
||||
log.error("演练场执行失败", e);
|
||||
|
||||
@@ -317,7 +320,6 @@ public class PlaygroundApi {
|
||||
PlaygroundTestResp response = PlaygroundTestResp.builder()
|
||||
.success(false)
|
||||
.error(errorMessage)
|
||||
.stackTrace(stackTrace)
|
||||
.executionTime(executionTime)
|
||||
.logs(respLogs)
|
||||
.build();
|
||||
@@ -328,14 +330,12 @@ public class PlaygroundApi {
|
||||
} catch (Exception e) {
|
||||
long executionTime = System.currentTimeMillis() - startTime;
|
||||
String errorMessage = e.getMessage();
|
||||
String stackTrace = getStackTrace(e);
|
||||
|
||||
log.error("演练场初始化失败", e);
|
||||
|
||||
PlaygroundTestResp response = PlaygroundTestResp.builder()
|
||||
.success(false)
|
||||
.error(errorMessage)
|
||||
.stackTrace(stackTrace)
|
||||
.executionTime(executionTime)
|
||||
.logs(new ArrayList<>())
|
||||
.build();
|
||||
@@ -346,8 +346,7 @@ public class PlaygroundApi {
|
||||
log.error("解析请求参数失败", e);
|
||||
promise.complete(JsonObject.mapFrom(PlaygroundTestResp.builder()
|
||||
.success(false)
|
||||
.error("解析请求参数失败: " + e.getMessage())
|
||||
.stackTrace(getStackTrace(e))
|
||||
.error("解析请求参数失败")
|
||||
.build()));
|
||||
}
|
||||
|
||||
@@ -696,18 +695,5 @@ public class PlaygroundApi {
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常堆栈信息
|
||||
*/
|
||||
private String getStackTrace(Throwable throwable) {
|
||||
if (throwable == null) {
|
||||
return "";
|
||||
}
|
||||
java.io.StringWriter sw = new java.io.StringWriter();
|
||||
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
|
||||
throwable.printStackTrace(pw);
|
||||
return sw.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,18 +125,18 @@ public class UserServiceImpl implements UserService {
|
||||
if (rows.size() == 0) {
|
||||
promise.complete(new JsonObject()
|
||||
.put("success", false)
|
||||
.put("message", "用户不存在"));
|
||||
.put("message", "用户名或密码错误"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Row row = rows.iterator().next();
|
||||
SysUser existUser = rowToUser(row);
|
||||
|
||||
|
||||
// 验证密码
|
||||
if (!PasswordUtil.checkPassword(user.getPassword(), existUser.getPassword())) {
|
||||
promise.complete(new JsonObject()
|
||||
.put("success", false)
|
||||
.put("message", "密码错误"));
|
||||
.put("message", "用户名或密码错误"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ public class UserServiceImpl implements UserService {
|
||||
log.error("登录查询失败", err);
|
||||
promise.complete(new JsonObject()
|
||||
.put("success", false)
|
||||
.put("message", "登录失败: " + err.getMessage()));
|
||||
.put("message", "登录失败,请稍后重试"));
|
||||
});
|
||||
|
||||
return promise.future();
|
||||
@@ -189,10 +189,10 @@ public class UserServiceImpl implements UserService {
|
||||
.execute(Tuple.of(username))
|
||||
.onSuccess(rows -> {
|
||||
if (rows.size() == 0) {
|
||||
promise.fail("用户不存在");
|
||||
promise.fail("用户名或密码错误");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Row row = rows.iterator().next();
|
||||
SysUser user = rowToUser(row);
|
||||
promise.complete(filterSensitiveInfo(user));
|
||||
@@ -296,10 +296,10 @@ public class UserServiceImpl implements UserService {
|
||||
.execute(Tuple.of(user.getUsername()))
|
||||
.onSuccess(rows -> {
|
||||
if (rows.size() == 0) {
|
||||
promise.fail("用户不存在");
|
||||
promise.fail("用户名或密码错误");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Row row = rows.iterator().next();
|
||||
SysUser existUser = rowToUser(row);
|
||||
|
||||
@@ -406,7 +406,7 @@ public class UserServiceImpl implements UserService {
|
||||
.onFailure(err -> {
|
||||
promise.complete(new JsonObject()
|
||||
.put("success", false)
|
||||
.put("message", "用户不存在"));
|
||||
.put("message", "认证失败,请重新登录"));
|
||||
});
|
||||
|
||||
return promise.future();
|
||||
|
||||
Reference in New Issue
Block a user