新增20个超进化安全模块 (17 new services + 1 test suite + Dashboard API): - JWT认证服务 (JwtAuthService): 基于HMAC-SHA256的token管理 - 反暴力破解 (BruteForceProtectionService): 渐进延迟+三层时间窗口检测 - 反机器人检测 (AntiBotDetectionService): MC玩家名模式+加入速度+多账号检测 - 反向Shell检测 (ReverseShellDetectionService): 50+ reverse shell模式匹配 - ARP欺骗检测 (ARPSpoofDetectionService): MAC变更+网关欺骗+重复MAC检测 - DNS隧道检测 (DNSTunnelDetectionService): 熵值分析+子域名编码+频率监控 - 漏洞签名检测 (ExploitSignatureService): Log4Shell/SQL注入/XSS等15+签名 - SSRF防护 (SSRFProtectionService): 内网IP/云元数据/编码绕过检测 - XXE防护 (XXEProtectionService): XML实体注入+十亿笑攻击检测 - CSP强制执行 (CSPEnforcementService): XSS/Clickjack防御+安全响应头 - 数据库防火墙 (DatabaseFirewallService): SQL注入+危险关键字+堆叠查询检测 - 数据防泄漏 (DataLossPreventionService): 12种敏感数据模式(邮箱/API Key/SSH Key等) - 内存保护 (MemoryProtectionService): JVM堆监控+GC过载+内存泄漏检测 - 进程注入检测 (ProcessInjectionDetectionService): 线程异常+内存映射+文件描述符 - 安全文件删除 (SecureFileDeletionService): DoD多pass覆写+擦除后删除 - 取证收集器 (ForensicsCollectorService): 进程/网络/文件/日志取证 - 事件响应 (IncidentResponseService): 5套Playbook(DDOS/爆破/入侵/MC漏洞/数据泄露) - 威胁狩猎 (ThreatHuntingService): 10种主动狩猎规则 - 合规扫描 (ComplianceScannerService): 7大类20+项合规检查 - 反破坏检测 (AntiGriefDetectionService): MC方块破坏/放置/实体/容器监控 API端点: DashboardController新增20+个安全API端点 测试: 64个测试全部通过 (12 existing + 52 new)
228 lines
9.4 KiB
Java
228 lines
9.4 KiB
Java
package com.aluer.security;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.io.*;
|
|
import java.nio.file.*;
|
|
import java.time.Instant;
|
|
import java.util.*;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
|
@Service
|
|
public class ForensicsCollectorService {
|
|
|
|
private final Map<String, ForensicsCase> cases = new ConcurrentHashMap<>();
|
|
private final Map<String, List<ForensicEvidence>> evidenceStore = new ConcurrentHashMap<>();
|
|
private final AtomicLong totalEvidenceCollected = new AtomicLong(0);
|
|
private final String evidenceDir = "./forensics";
|
|
|
|
public ForensicsCollectorService() {
|
|
try {
|
|
Files.createDirectories(Path.of(evidenceDir));
|
|
} catch (IOException ignored) {}
|
|
}
|
|
|
|
public ForensicsCase openCase(String caseName, String reason, String operator) {
|
|
String caseId = "CASE-" + Instant.now().getEpochSecond() + "-" + randomHex(6);
|
|
ForensicsCase fCase = new ForensicsCase(caseId, caseName, reason, operator, Instant.now());
|
|
cases.put(caseId, fCase);
|
|
evidenceStore.put(caseId, new ArrayList<>());
|
|
return fCase;
|
|
}
|
|
|
|
public ForensicEvidence collectProcessList(String caseId) {
|
|
StringBuilder sb = new StringBuilder();
|
|
try {
|
|
Process proc = Runtime.getRuntime().exec(new String[]{"sh", "-c", "ps aux"});
|
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
|
|
String line;
|
|
while ((line = reader.readLine()) != null) {
|
|
sb.append(line).append('\n');
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
sb.append("ERROR: ").append(e.getMessage());
|
|
}
|
|
return addEvidence(caseId, "PROCESS_LIST", sb.toString());
|
|
}
|
|
|
|
public ForensicEvidence collectNetworkConnections(String caseId) {
|
|
StringBuilder sb = new StringBuilder();
|
|
try {
|
|
Process proc = Runtime.getRuntime().exec(new String[]{"sh", "-c", "ss -tunap 2>/dev/null || netstat -tunap 2>/dev/null"});
|
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
|
|
String line;
|
|
while ((line = reader.readLine()) != null) {
|
|
sb.append(line).append('\n');
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
sb.append("ERROR: ").append(e.getMessage());
|
|
}
|
|
return addEvidence(caseId, "NETWORK_CONNECTIONS", sb.toString());
|
|
}
|
|
|
|
public ForensicEvidence collectOpenFiles(String caseId) {
|
|
StringBuilder sb = new StringBuilder();
|
|
try {
|
|
Process proc = Runtime.getRuntime().exec(new String[]{"sh", "-c", "lsof -nP 2>/dev/null | head -500"});
|
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
|
|
String line;
|
|
while ((line = reader.readLine()) != null) {
|
|
sb.append(line).append('\n');
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
sb.append("ERROR: ").append(e.getMessage());
|
|
}
|
|
return addEvidence(caseId, "OPEN_FILES", sb.toString());
|
|
}
|
|
|
|
public ForensicEvidence collectSystemLogs(String caseId) {
|
|
StringBuilder sb = new StringBuilder();
|
|
try {
|
|
Process proc = Runtime.getRuntime().exec(new String[]{"sh", "-c", "journalctl -n 200 --no-pager 2>/dev/null || dmesg | tail -200"});
|
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
|
|
String line;
|
|
while ((line = reader.readLine()) != null) {
|
|
sb.append(line).append('\n');
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
sb.append("ERROR: ").append(e.getMessage());
|
|
}
|
|
return addEvidence(caseId, "SYSTEM_LOGS", sb.toString());
|
|
}
|
|
|
|
public ForensicEvidence collectMCPServerLogs(String caseId, String mcLogPath) {
|
|
if (mcLogPath == null) mcLogPath = "/opt/minecraft/logs/latest.log";
|
|
try {
|
|
String content = Files.readString(Path.of(mcLogPath));
|
|
return addEvidence(caseId, "MC_SERVER_LOG", content);
|
|
} catch (Exception e) {
|
|
return addEvidence(caseId, "MC_SERVER_LOG", "ERROR: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
public ForensicEvidence collectTimestampSnapshot(String caseId) {
|
|
Map<String, Object> snapshot = new LinkedHashMap<>();
|
|
snapshot.put("systemTime", Instant.now().toString());
|
|
snapshot.put("uptime", System.getProperty("os.name"));
|
|
snapshot.put("javaVersion", System.getProperty("java.version"));
|
|
snapshot.put("availableProcessors", Runtime.getRuntime().availableProcessors());
|
|
snapshot.put("freeMemory", Runtime.getRuntime().freeMemory());
|
|
snapshot.put("totalMemory", Runtime.getRuntime().totalMemory());
|
|
return addEvidence(caseId, "TIMESTAMP_SNAPSHOT", snapshot.toString());
|
|
}
|
|
|
|
public ForensicsCase closeCase(String caseId, String conclusion, String operator) {
|
|
ForensicsCase fCase = cases.get(caseId);
|
|
if (fCase != null) {
|
|
fCase.close(conclusion, operator);
|
|
saveCaseToDisk(fCase);
|
|
}
|
|
return fCase;
|
|
}
|
|
|
|
public Map<String, Object> getStatus() {
|
|
Map<String, Object> status = new LinkedHashMap<>();
|
|
status.put("openCases", cases.values().stream().filter(c -> !c.closed).count());
|
|
status.put("totalCases", cases.size());
|
|
status.put("totalEvidence", totalEvidenceCollected.get());
|
|
List<Map<String, Object>> caseList = new ArrayList<>();
|
|
for (ForensicsCase c : cases.values()) {
|
|
Map<String, Object> m = new LinkedHashMap<>();
|
|
m.put("id", c.caseId);
|
|
m.put("name", c.caseName);
|
|
m.put("reason", c.reason);
|
|
m.put("openTime", c.openTime.toString());
|
|
m.put("closed", c.closed);
|
|
m.put("evidenceCount", evidenceStore.getOrDefault(c.caseId, List.of()).size());
|
|
caseList.add(m);
|
|
}
|
|
caseList.sort((a, b) -> b.get("openTime").toString().compareTo(a.get("openTime").toString()));
|
|
status.put("cases", caseList);
|
|
return status;
|
|
}
|
|
|
|
public long getTotalEvidence() { return totalEvidenceCollected.get(); }
|
|
|
|
private ForensicEvidence addEvidence(String caseId, String type, String content) {
|
|
ForensicEvidence evidence = new ForensicEvidence(caseId, type, content, Instant.now());
|
|
evidenceStore.computeIfAbsent(caseId, k -> new ArrayList<>()).add(evidence);
|
|
totalEvidenceCollected.incrementAndGet();
|
|
return evidence;
|
|
}
|
|
|
|
private void saveCaseToDisk(ForensicsCase fCase) {
|
|
try {
|
|
Path caseDir = Path.of(evidenceDir, fCase.caseId);
|
|
Files.createDirectories(caseDir);
|
|
StringBuilder report = new StringBuilder();
|
|
report.append("=== FORENSICS CASE REPORT ===\n");
|
|
report.append("Case ID: ").append(fCase.caseId).append('\n');
|
|
report.append("Name: ").append(fCase.caseName).append('\n');
|
|
report.append("Reason: ").append(fCase.reason).append('\n');
|
|
report.append("Operator: ").append(fCase.operator).append('\n');
|
|
report.append("Opened: ").append(fCase.openTime).append('\n');
|
|
report.append("Closed: ").append(fCase.closeTime).append('\n');
|
|
report.append("Conclusion: ").append(fCase.conclusion).append('\n');
|
|
report.append("\n--- EVIDENCE ---\n\n");
|
|
List<ForensicEvidence> items = evidenceStore.getOrDefault(fCase.caseId, List.of());
|
|
for (ForensicEvidence e : items) {
|
|
report.append("[").append(e.type).append("] ").append(e.timestamp).append('\n');
|
|
report.append(e.content).append("\n---\n\n");
|
|
}
|
|
Files.writeString(caseDir.resolve("report.txt"), report.toString());
|
|
} catch (IOException ignored) {}
|
|
}
|
|
|
|
private String randomHex(int len) {
|
|
byte[] bytes = new byte[len];
|
|
new Random().nextBytes(bytes);
|
|
StringBuilder sb = new StringBuilder();
|
|
for (byte b : bytes) sb.append(String.format("%02x", b));
|
|
return sb.toString().substring(0, len);
|
|
}
|
|
|
|
public static class ForensicsCase {
|
|
public final String caseId;
|
|
public final String caseName;
|
|
public final String reason;
|
|
public final String operator;
|
|
public final Instant openTime;
|
|
public Instant closeTime;
|
|
public String conclusion;
|
|
public boolean closed;
|
|
|
|
ForensicsCase(String caseId, String caseName, String reason, String operator, Instant openTime) {
|
|
this.caseId = caseId;
|
|
this.caseName = caseName;
|
|
this.reason = reason;
|
|
this.operator = operator;
|
|
this.openTime = openTime;
|
|
}
|
|
|
|
void close(String conclusion, String operator) {
|
|
this.conclusion = conclusion;
|
|
this.closeTime = Instant.now();
|
|
this.closed = true;
|
|
}
|
|
}
|
|
|
|
public static class ForensicEvidence {
|
|
public final String caseId;
|
|
public final String type;
|
|
public final String content;
|
|
public final Instant timestamp;
|
|
|
|
ForensicEvidence(String caseId, String type, String content, Instant timestamp) {
|
|
this.caseId = caseId;
|
|
this.type = type;
|
|
this.content = content;
|
|
this.timestamp = timestamp;
|
|
}
|
|
}
|
|
}
|