mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 12:23:03 +00:00
232 lines
6.7 KiB
Markdown
232 lines
6.7 KiB
Markdown
# 🔐 DoS漏洞修复报告
|
||
|
||
## 修复日期
|
||
2025-11-29
|
||
|
||
## 修复漏洞
|
||
|
||
### 1. ✅ 代码长度限制(防止内存炸弹)
|
||
|
||
**漏洞描述**:
|
||
没有对JavaScript代码长度限制,攻击者可以提交超大代码或创建大量数据消耗内存。
|
||
|
||
**修复内容**:
|
||
- 添加 `MAX_CODE_LENGTH = 128 * 1024` (128KB) 常量
|
||
- 在 `PlaygroundApi.test()` 方法中添加代码长度验证
|
||
- 在 `PlaygroundApi.saveParser()` 方法中添加代码长度验证
|
||
|
||
**修复文件**:
|
||
```
|
||
web-service/src/main/java/cn/qaiu/lz/web/controller/PlaygroundApi.java
|
||
```
|
||
|
||
**修复代码**:
|
||
```java
|
||
private static final int MAX_CODE_LENGTH = 128 * 1024; // 128KB
|
||
|
||
// 代码长度验证
|
||
if (jsCode.length() > MAX_CODE_LENGTH) {
|
||
promise.complete(JsonResult.error("代码长度超过限制(最大128KB),当前长度: " + jsCode.length() + " 字节").toJsonObject());
|
||
return promise.future();
|
||
}
|
||
```
|
||
|
||
**测试POC**:
|
||
参见 `web-service/src/test/resources/playground-dos-tests.http` - 测试2
|
||
|
||
---
|
||
|
||
### 2. ✅ JavaScript执行超时(防止无限循环DoS)
|
||
|
||
**漏洞描述**:
|
||
JavaScript执行没有超时限制,攻击者可以提交包含无限循环的代码导致线程被长期占用。
|
||
|
||
**修复内容**:
|
||
- 添加 `EXECUTION_TIMEOUT_SECONDS = 30` 秒超时常量
|
||
- 使用 `CompletableFuture.orTimeout()` 添加超时机制
|
||
- 超时后立即返回错误,不影响主线程
|
||
- 修复三个执行方法:`executeParseAsync()`, `executeParseFileListAsync()`, `executeParseByIdAsync()`
|
||
- **前端添加危险代码检测**:检测 `while(true)`, `for(;;)` 等无限循环模式并警告用户
|
||
- **使用临时WorkerExecutor**:每个请求创建独立的executor,执行完毕后关闭,避免阻塞的线程继续输出日志
|
||
|
||
**修复文件**:
|
||
```
|
||
parser/src/main/java/cn/qaiu/parser/customjs/JsPlaygroundExecutor.java
|
||
web-front/src/views/Playground.vue
|
||
```
|
||
|
||
**⚠️ 重要限制与优化**:
|
||
由于 **Nashorn 引擎的限制**,超时机制表现为:
|
||
1. ✅ 在30秒后向客户端返回超时错误
|
||
2. ✅ 记录超时日志
|
||
3. ✅ 关闭临时WorkerExecutor,停止输出阻塞警告日志
|
||
4. ❌ **无法中断正在执行的JavaScript代码**
|
||
|
||
**优化措施**(2025-11-29更新):
|
||
- ✅ **临时Executor机制**:每个请求使用独立的临时WorkerExecutor
|
||
- ✅ **自动清理**:执行完成或超时后自动关闭executor
|
||
- ✅ **避免日志污染**:关闭executor后不再输出BlockedThreadChecker警告
|
||
- ✅ **资源隔离**:被阻塞的线程被放弃,不影响新请求
|
||
|
||
这意味着:
|
||
- ✅ 客户端会及时收到超时错误
|
||
- ✅ 日志不会持续滚动输出阻塞警告
|
||
- ⚠️ 被阻塞的线程仍在后台执行(但已被隔离)
|
||
- ⚠️ 频繁的无限循环攻击会创建大量线程(建议监控)
|
||
|
||
**缓解措施**:
|
||
1. ✅ 前端检测危险代码模式(已实现)
|
||
2. ✅ 用户确认对话框(已实现)
|
||
3. ✅ Worker线程池隔离(避免影响主服务)
|
||
4. ✅ 超时后返回错误给用户(已实现)
|
||
5. ⚠️ 建议监控线程阻塞告警
|
||
6. ⚠️ 必要时重启服务释放被阻塞的线程
|
||
|
||
**修复代码**:
|
||
```java
|
||
private static final long EXECUTION_TIMEOUT_SECONDS = 30;
|
||
|
||
// 添加超时处理
|
||
executionFuture.toCompletionStage()
|
||
.toCompletableFuture()
|
||
.orTimeout(EXECUTION_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||
.whenComplete((result, error) -> {
|
||
if (error != null) {
|
||
if (error instanceof java.util.concurrent.TimeoutException) {
|
||
String timeoutMsg = "JavaScript执行超时(超过" + EXECUTION_TIMEOUT_SECONDS + "秒),可能存在无限循环";
|
||
playgroundLogger.errorJava(timeoutMsg);
|
||
log.error(timeoutMsg);
|
||
promise.fail(new RuntimeException(timeoutMsg));
|
||
} else {
|
||
promise.fail(error);
|
||
}
|
||
} else {
|
||
promise.complete(result);
|
||
}
|
||
});
|
||
```
|
||
|
||
**测试POC**:
|
||
参见 `web-service/src/test/resources/playground-dos-tests.http` - 测试3, 4, 5
|
||
|
||
---
|
||
|
||
## 修复效果
|
||
|
||
### 代码长度限制
|
||
- ✅ 超过128KB的代码会立即被拒绝
|
||
- ✅ 返回友好的错误提示
|
||
- ✅ 防止内存炸弹攻击
|
||
|
||
### 执行超时机制
|
||
- ✅ 无限循环会在30秒后超时
|
||
- ✅ 超时不会阻塞主线程
|
||
- ✅ 超时后立即返回错误给用户
|
||
- ⚠️ **注意**:由于Nashorn引擎限制,被阻塞的worker线程无法被立即中断,会继续执行直到完成或JVM关闭
|
||
|
||
---
|
||
|
||
## 测试验证
|
||
|
||
### 测试文件
|
||
```
|
||
web-service/src/test/resources/playground-dos-tests.http
|
||
```
|
||
|
||
### 测试用例
|
||
1. ✅ 正常代码执行 - 应该成功
|
||
2. ✅ 代码长度超限 - 应该被拒绝
|
||
3. ✅ 无限循环攻击 - 应该30秒超时
|
||
4. ✅ 内存炸弹攻击 - 应该30秒超时
|
||
5. ✅ 递归栈溢出 - 应该被捕获
|
||
6. ✅ 保存解析器验证 - 应该成功
|
||
|
||
### 如何运行测试
|
||
1. 启动服务器:`./bin/run.sh`
|
||
2. 使用HTTP客户端或IntelliJ IDEA的HTTP Client运行测试
|
||
3. 观察响应结果
|
||
|
||
---
|
||
|
||
## 其他建议(未实现)
|
||
|
||
### 3. HTTP请求次数限制(可选)
|
||
**建议**:限制单次执行中的HTTP请求次数(例如最多20次)
|
||
|
||
```java
|
||
// JsHttpClient.java
|
||
private static final int MAX_REQUESTS_PER_EXECUTION = 20;
|
||
private final AtomicInteger requestCount = new AtomicInteger(0);
|
||
|
||
private void checkRequestLimit() {
|
||
if (requestCount.incrementAndGet() > MAX_REQUESTS_PER_EXECUTION) {
|
||
throw new RuntimeException("HTTP请求次数超过限制");
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 单IP创建限制(可选)
|
||
**建议**:限制单个IP最多创建10个解析器
|
||
|
||
```java
|
||
// PlaygroundApi.java
|
||
private static final int MAX_PARSERS_PER_IP = 10;
|
||
```
|
||
|
||
### 5. 过滤错误堆栈(可选)
|
||
**建议**:只返回错误消息,不返回完整的Java堆栈信息
|
||
|
||
---
|
||
|
||
## 安全状态
|
||
|
||
| 漏洞 | 修复状态 | 测试状态 |
|
||
|------|---------|----------|
|
||
| 代码长度限制 | ✅ 已修复 | ✅ 已测试 |
|
||
| 执行超时 | ✅ 已修复 | ✅ 已测试 |
|
||
| HTTP请求滥用 | ⚠️ 未修复 | - |
|
||
| 数据库污染 | ⚠️ 未修复 | - |
|
||
| 信息泄露 | ⚠️ 未修复 | - |
|
||
|
||
---
|
||
|
||
## 性能影响
|
||
|
||
- **代码长度检查**:O(1) - 几乎无性能影响
|
||
- **执行超时**:极小影响 - 仅添加超时监听器
|
||
|
||
---
|
||
|
||
## 向后兼容性
|
||
|
||
✅ 完全兼容
|
||
- 不影响现有正常代码执行
|
||
- 只拒绝恶意或超大代码
|
||
- API接口不变
|
||
|
||
---
|
||
|
||
## 部署建议
|
||
|
||
1. ✅ 代码已编译通过
|
||
2. ⚠️ 建议在测试环境验证后再部署生产
|
||
3. ⚠️ 建议配置监控告警,监测超时频率
|
||
4. ⚠️ 考虑添加IP限流或验证码防止滥用
|
||
|
||
---
|
||
|
||
## 更新记录
|
||
|
||
**2025-11-29**
|
||
- 添加128KB代码长度限制
|
||
- 添加30秒JavaScript执行超时
|
||
- 创建DoS攻击测试用例
|
||
- 编译验证通过
|
||
|
||
---
|
||
|
||
**修复人员**: AI Assistant
|
||
**审核状态**: ⚠️ 待人工审核
|
||
**优先级**: 🔴 高 (建议尽快部署)
|
||
|