12 KiB
JavaScript执行器安全测试指南
概述
本文档提供了一套完整的安全测试用例,用于验证JavaScript演练场执行器的安全性。这些测试旨在检测潜在的安全漏洞,包括但不限于:
- 系统命令执行
- 文件系统访问
- 反射攻击
- 网络攻击 (SSRF)
- JVM退出
- DOS攻击
- 内存溢出
⚠️ 重要警告
这些测试用例包含危险代码,仅用于安全测试目的!
- ❌ 不要在生产环境执行这些测试
- ❌ 不要将这些代码暴露给未授权用户
- ✅ 仅在隔离的测试环境中执行
- ✅ 执行前确保有完整的系统备份
测试方式
方式1: JUnit单元测试
使用提供的JUnit测试类 SecurityTest.java:
cd parser
mvn test -Dtest=SecurityTest
方式2: HTTP接口测试
使用提供的HTTP测试文件 playground-security-tests.http:
- 启动应用服务器
- 在IDE中打开
web-service/src/test/resources/playground-security-tests.http - 逐个执行测试用例
或使用curl命令:
curl -X POST http://localhost:9000/v2/playground/test \
-H "Content-Type: application/json" \
-d @test-case.json
测试用例说明
1. 系统命令执行测试 🔴 高危
测试目标: 验证是否能通过Java的Runtime或ProcessBuilder执行系统命令
危险级别: ⚠️⚠️⚠️ 极高
测试内容:
- 尝试使用
Runtime.getRuntime().exec()执行shell命令 - 尝试使用
ProcessBuilder执行系统命令 - 尝试读取命令执行结果
预期结果:
- ✅ 安全: 无法访问
Java.type()或相关类 - ❌ 危险: 成功执行系统命令
示例攻击:
var Runtime = Java.type('java.lang.Runtime');
var process = Runtime.getRuntime().exec('whoami');
2. 文件系统访问测试 🔴 高危
测试目标: 验证是否能读写本地文件系统
危险级别: ⚠️⚠️⚠️ 极高
测试内容:
- 尝试读取敏感文件 (
/etc/passwd, 数据库文件等) - 尝试写入文件到系统目录
- 尝试删除文件
预期结果:
- ✅ 安全: 无法访问文件系统API
- ❌ 危险: 成功读写文件
示例攻击:
var Files = Java.type('java.nio.file.Files');
var content = Files.readAllLines(Paths.get('/etc/passwd'));
3. 系统属性访问测试 🟡 中危
测试目标: 验证是否能访问系统属性和环境变量
危险级别: ⚠️⚠️ 高
测试内容:
- 读取系统属性 (
user.home,user.name,java.version) - 读取环境变量 (
PATH,JAVA_HOME, API密钥等) - 修改系统属性
预期结果:
- ✅ 安全: 无法访问System类
- ❌ 危险: 成功获取敏感信息
潜在风险: 可能泄露系统配置、用户信息、API密钥等敏感数据
4. 反射攻击测试 🔴 高危
测试目标: 验证是否能通过反射绕过访问控制
危险级别: ⚠️⚠️⚠️ 极高
测试内容:
- 使用
Class.forName()加载任意类 - 通过反射调用私有方法
- 修改final字段
- 获取ClassLoader
预期结果:
- ✅ 安全: 无法使用反射API
- ❌ 危险: 成功绕过访问控制
示例攻击:
var Class = Java.type('java.lang.Class');
var systemClass = Class.forName('java.lang.System');
var methods = systemClass.getDeclaredMethods();
5. 网络Socket攻击测试 🔴 高危
测试目标: 验证是否能创建任意网络连接
危险级别: ⚠️⚠️⚠️ 极高
测试内容:
- 创建Socket连接到任意主机
- 使用URL/URLConnection访问任意地址
- 端口扫描
预期结果:
- ✅ 安全: 无法创建网络连接
- ❌ 危险: 可以连接任意主机端口
潜在风险: 可用于端口扫描、内网渗透、绕过防火墙
6. JVM退出攻击测试 🔴 高危
测试目标: 验证是否能终止JVM进程
危险级别: ⚠️⚠️⚠️ 极高
测试内容:
- 调用
System.exit() - 调用
Runtime.halt() - 触发致命错误
预期结果:
- ✅ 安全: 无法退出JVM
- ❌ 危险: 成功终止应用
影响: 导致整个应用崩溃,拒绝服务
7. HTTP客户端SSRF测试 🟡 中危
测试目标: 验证注入的httpClient是否可被滥用
危险级别: ⚠️⚠️ 高
测试内容:
- 访问内网地址 (127.0.0.1, 192.168.x.x, 10.x.x.x)
- 访问云服务元数据API (169.254.169.254)
- 访问本地服务端口
- 访问管理后台
预期结果:
- ✅ 最佳: HTTP客户端有白名单限制
- ⚠️ 可接受: 可以访问外网但不能访问内网
- ❌ 危险: 可以访问任意地址包括内网
潜在风险: SSRF攻击、内网信息泄露、云服务凭证窃取
8. 对象滥用测试 🟡 中危
测试目标: 验证注入的Java对象是否可被反射访问
危险级别: ⚠️⚠️ 高
测试内容:
- 通过反射访问注入对象的私有字段
- 调用对象的非公开方法
- 修改对象内部状态
预期结果:
- ✅ 安全: 无法通过反射访问对象
- ⚠️ 可接受: 只能访问公开API
- ❌ 危险: 可以访问和修改内部状态
9. DOS攻击测试 🟡 中危
测试目标: 验证是否存在执行时间限制
危险级别: ⚠️⚠️ 高
测试内容:
- 无限循环
- 长时间计算
- 递归调用
预期结果:
- ✅ 安全: 有超时机制,自动中断执行
- ❌ 危险: 可以无限执行
影响: 消耗CPU资源,导致服务响应缓慢或拒绝服务
10. 内存溢出测试 🟡 中危
测试目标: 验证是否存在内存使用限制
危险级别: ⚠️⚠️ 高
测试内容:
- 创建大量对象
- 分配大数组
- 递归创建深层对象
预期结果:
- ✅ 安全: 有内存限制,防止OOM
- ❌ 危险: 可以无限分配内存
影响: 导致内存溢出,应用崩溃
安全建议
当前Nashorn引擎的安全问题
Nashorn引擎默认允许JavaScript访问所有Java类,这是一个严重的安全隐患。以下是建议的安全措施:
1. 使用ClassFilter限制类访问 🔒 必须
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
public class SecurityClassFilter implements ClassFilter {
@Override
public boolean exposeToScripts(String className) {
// 黑名单:禁止访问危险类
if (className.startsWith("java.lang.Runtime") ||
className.startsWith("java.lang.ProcessBuilder") ||
className.startsWith("java.io.File") ||
className.startsWith("java.nio.file") ||
className.startsWith("java.lang.System") ||
className.startsWith("java.lang.Class") ||
className.startsWith("java.lang.reflect") ||
className.startsWith("java.net.Socket") ||
className.startsWith("java.net.URL")) {
return false;
}
// 白名单:只允许特定的类
// return className.startsWith("允许的包名");
return false; // 默认拒绝所有
}
}
// 使用ClassFilter创建引擎
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine(new SecurityClassFilter());
2. 设置执行超时 ⏱️ 强烈推荐
// 使用Future + timeout
Future<?> future = executor.submit(() -> {
engine.eval(jsCode);
});
try {
future.get(30, TimeUnit.SECONDS); // 30秒超时
} catch (TimeoutException e) {
future.cancel(true);
throw new RuntimeException("脚本执行超时");
}
3. 限制内存使用 💾 推荐
// 在Worker线程中执行,限制堆大小
// 启动参数: -Xmx512m
4. 沙箱隔离 🏝️ 强烈推荐
考虑使用以下方案:
- GraalVM JavaScript: 更安全的JavaScript引擎,支持沙箱
- Docker容器隔离: 在容器中执行不信任的代码
- Java SecurityManager: 配置安全策略文件
5. HTTP客户端访问控制 🌐 必须
// 在JsHttpClient中添加URL验证
private boolean isAllowedUrl(String url) {
// 禁止访问内网地址
if (url.matches(".*\\b(127\\.0\\.0\\.1|localhost|192\\.168\\.|10\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.).*")) {
return false;
}
// 禁止访问云服务元数据
if (url.contains("169.254.169.254")) {
return false;
}
// 白名单检查
// return allowedDomains.contains(getDomain(url));
return true;
}
6. 输入验证 ✅ 必须
// 验证JavaScript代码
private void validateJsCode(String jsCode) {
// 检查代码长度
if (jsCode.length() > 100000) {
throw new IllegalArgumentException("代码过长");
}
// 检查危险关键词
List<String> dangerousKeywords = Arrays.asList(
"Java.type",
"getClass",
"getRuntime",
"exec(",
"ProcessBuilder",
"System.exit",
"Runtime.halt"
);
for (String keyword : dangerousKeywords) {
if (jsCode.contains(keyword)) {
throw new SecurityException("代码包含危险操作: " + keyword);
}
}
}
7. 监控和日志 📊 必须
// 记录所有执行的脚本
log.info("执行脚本 - 用户: {}, IP: {}, 代码哈希: {}",
userId, clientIp, DigestUtils.md5Hex(jsCode));
// 监控异常行为
if (executionTime > 10000) {
log.warn("脚本执行时间过长: {}ms", executionTime);
}
8. 迁移到GraalVM 🚀 长期建议
Nashorn已在JDK 15中废弃,建议迁移到GraalVM JavaScript:
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
<version>23.0.0</version>
</dependency>
GraalVM提供更好的安全性和性能:
- 默认沙箱隔离
- 无法访问Java类(除非显式允许)
- 更好的性能
- 活跃维护
测试检查清单
执行安全测试时,请确认以下检查项:
- 测试1: 系统命令执行 - 应该失败
- 测试2: 文件系统访问 - 应该失败
- 测试3: 系统属性访问 - 应该失败
- 测试4: 反射攻击 - 应该失败
- 测试5: 网络Socket - 应该失败
- 测试6: JVM退出 - 应该失败
- 测试7: SSRF攻击 - 应该部分失败(禁止内网访问)
- 测试8: 对象滥用 - 应该部分失败(只能访问公开API)
- 测试9: DOS攻击 - 应该超时中断
- 测试10: 内存溢出 - 应该抛出OOM或限制
安全评估标准
🟢 安全 (A级)
- 所有高危测试都失败
- 有完善的ClassFilter
- 有超时和内存限制
- HTTP客户端有访问控制
🟡 基本安全 (B级)
- 大部分高危测试失败
- 无法执行系统命令和文件操作
- 有部分访问控制
🟠 存在风险 (C级)
- 某些中危测试通过
- 缺少超时或内存限制
- HTTP客户端无限制
🔴 严重不安全 (D级)
- 高危测试通过
- 可以执行系统命令
- 可以读写文件系统
- 不应在生产环境使用
参考资料
- OWASP - Server Side Request Forgery
- Nashorn Security Guide
- GraalVM JavaScript Security
- Java SecurityManager Documentation
联系方式
如果发现新的安全漏洞,请通过安全渠道报告,不要公开披露。
免责声明: 本文档仅用于安全测试和教育目的。任何人使用这些测试用例造成的损害,作者概不负责。