# 🔐 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 **审核状态**: ⚠️ 待人工审核 **优先级**: 🔴 高 (建议尽快部署)