检测与 SIEM
这一课你将能做到
- 解释 SIEM、IDS、IPS、EDR 四者的分工与互补关系
- 为本站(Nginx + Spring Boot + MySQL + Redis)设计一套日志收集方案,明确哪些日志必须采集
- 写出 3 条可落地的检测规则(暴力破解、异常流量、权限变更),并配置告警
为什么要学
没有 100% 的安全。防火墙会被绕过、漏洞会被发现、凭证会泄漏。问题不是「会不会被攻破」,而是「被攻破后多久能发现」。全球统计:入侵平均驻留时间(dwell time)以「月」计——攻击者在系统里潜伏数月才被发现。
人话:检测是「监控系统」,SIEM 是「把所有监控数据汇总的指挥中心」。没有检测,入侵者在你数据库里拷了三个月数据你都不知道。有了检测,可能 5 分钟内就收到告警并自动封锁。
先建立图像
检测层技术栈:
日志源 采集 存储/分析 响应
───────────── ────────────────── ───────────────────── ──────────────
Nginx access.log ──┐ │
App 应用日志 ──┼──→ Filebeat ──→ Elasticsearch ──→ Kibana │
MySQL binlog ──┤ (采集) (索引) (可视化) │
Redis slowlog ──┘ │
│
Spring Boot 审计日志 ────────────────────────────┐ │
│ │
检测规则引擎 ←──────┘
(ElastAlert/Wazuh)
│
▼
告警: Slack/钉钉/邮件
自动化: 封 IP/改密/隔离
关键日志清单(必须采集):
Nginx:
access.log —— 谁、什么时间、访问了什么、状态码、User-Agent
error.log —— 4xx/5xx 异常、后端超时
Spring Boot:
application.log —— 登录成功/失败、权限变更、敏感操作
audit.log —— 管理员操作审计(不可篡改)
security.log —— 401/403、JWT 过期、限流触发
MySQL:
general log —— 谁执行了什么 SQL(采样,性能考虑)
slow log —— 慢查询(可能是注入扫描)
binlog —— 数据变更(溯源)
Redis:
slowlog —— 危险命令调用
monitor —— 实时命令流(仅诊断时开)
系统:
auth.log / secure —— SSH 登录成功/失败、sudo 使用
cron.log —— 定时任务执行
跟着做
步骤 1:统一日志格式(Spring Boot)
让日志机器可读(JSON),不要靠人眼扫:
<!-- logback-spring.xml -->
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>traceId</includeMdcKeyName>
<includeMdcKeyName>userId</includeMdcKeyName>
</encoder>
</appender>
自定义 MDC 过滤器注入 userId:
@Component
@Order(1)
public class UserIdMdcFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws ServletException, IOException {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) {
MDC.put("userId", auth.getName());
}
MDC.put("traceId", UUID.randomUUID().toString().substring(0, 8));
try {
chain.doFilter(req, res);
} finally {
MDC.clear();
}
}
}
现在日志每行都是 JSON,含 userId、traceId,SIEM 可以直接解析查询。
步骤 2:记录安全事件日志
@Component
public class SecurityAuditLogger {
private static final Logger auditLog = LoggerFactory.getLogger("AUDIT");
public void logLoginSuccess(String username, String ip) {
auditLog.info("LOGIN_SUCCESS user={} ip={}", username, ip);
}
public void logLoginFailure(String username, String ip, String reason) {
auditLog.warn("LOGIN_FAILURE user={} ip={} reason={}", username, ip, reason);
}
public void logAdminAction(String adminUser, String action, String target, String ip) {
// 管理员操作单独文件,权限收紧
auditLog.info("ADMIN_ACTION admin={} action={} target={} ip={}",
adminUser, action, target, ip);
}
public void logPrivilegeChange(String userId, String oldRole, String newRole, String operator) {
auditLog.warn("PRIV_CHANGE user={} {}→{} by={}", userId, oldRole, newRole, operator);
}
}
public String login(String username, String password, String ip) {
try {
// ... 验证逻辑 ...
auditLogger.logLoginSuccess(username, ip);
return token;
} catch (BadCredentialsException e) {
auditLogger.logLoginFailure(username, ip, "bad_credentials");
throw e;
}
}
步骤 3:用 Filebeat + Elasticsearch 搭最小 SIEM
docker-compose 最小化部署(测试环境):
version: '3'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false # 测试环境,生产必开
ports:
- "9200:9200"
kibana:
image: docker.elastic.co/kibana/kibana:8.12.0
ports:
- "5601:5601"
filebeat:
image: docker.elastic.co/beats/filebeat:8.12.0
volumes:
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml
- /var/log/nginx:/var/log/nginx:ro # 采集 Nginx 日志
- ./logs:/app/logs:ro # 采集应用日志
depends_on:
- elasticsearch
filebeat.inputs:
- type: filestream
paths:
- /var/log/nginx/access.log
fields:
log_type: nginx-access
parsers:
- ndjson: # 如果 Nginx 配了 JSON 格式日志
target: ""
- type: filestream
paths:
- /app/logs/*.log
fields:
log_type: app
parsers:
- ndjson:
target: ""
output.elasticsearch:
hosts: ["elasticsearch:9200"]
步骤 4:写 3 条检测规则
# ElastAlert 规则示例 (rules/brute_force.yaml)
name: Login brute force
type: frequency
index: logs-*
num_events: 5 # 5 次失败
timeframe:
minutes: 1 # 1 分钟内
filter:
- term:
log_type: app
- term:
message: "LOGIN_FAILURE"
query_key:
- ip # 按 IP 分组
alert:
- slack:
slack_webhook_url: "https://hooks.slack.com/..."
name: Admin action from unusual IP
type: any
index: logs-*
filter:
- term:
message: "ADMIN_ACTION"
- not:
term:
ip: "203.0.113.0/24" # 办公网 IP 段
alert:
- email
规则 3:大量 4xx/5xx(可能是扫描器或攻击)
name: Error rate spike
type: spike
index: logs-nginx-*
threshold: 50
timeframe:
minutes: 5
spike_height: 3 # 错误数是平时的 3 倍
spike_type: up
filter:
- range:
status:
gte: 400
alert:
- slack
步骤 5:配置告警到钉钉/Slack
# 钉钉机器人告警
alert:
- "alertmanager" # 或直接 webhook
关键原则:告警必须可行动。「有人登录了」不是告警(太吵),「同 IP 1 分钟 5 次失败」是告警(该封 IP)。告警要包含:什么、谁、哪里、多严重、下一步建议。
讲清楚要点
-
IDS 和 IPS 的区别:IDS(入侵检测)只报警,不拦截;IPS(入侵防御)在线路上,实时阻断。现代边界用 IPS(WAF),内部用 IDS(EDR)监控。
-
SIEM 的核心是关联:单看「登录失败」是噪音,「登录失败 5 次 + 第 6 次成功 + 来自从未见过的国家」是事件。SIEM 把多源日志关联成安全事件。
-
日志的完整性比数量重要:如果应用账号能 UPDATE/DELETE 审计日志,攻击者就能抹掉痕迹。审计日志应该是「只追加」——写入后不可改,用 WAL 或独立日志服务保证。
-
EDR 是主机层的 SIEM:传统 SIEM 看网络流量和应用日志,EDR 看进程、文件、注册表(Windows)/系统调用(Linux)。主机层面检测恶意行为(如奇怪进程注入)。
-
告警疲劳是检测系统最大的敌人:100 条告警/天,人会在第 3 天开始全部忽略。只保留高置信度规则,每条告警都必须有明确的响应动作。宁可漏报,不可滥报。
你可能会懵的地方
-
SIEM 一定要买商业产品吗?
不需要。Elastic Stack(ELK)+ ElastAlert + Wazuh(开源 HIDS)就能搭一套覆盖中小企业需求的开源 SIEM。商业产品(Splunk、QRadar)贵在预置规则、技术支持、合规报告。
-
SOAR 是什么?
Security Orchestration, Automation and Response——安全编排自动化响应。SIEM 负责「发现」,SOAR 负责「处置」:检测到暴力破解后,SOAR 自动调用防火墙 API 封 IP、给用户发通知、创建工单。是 SIEM 的下一层。
-
日志要保留多久?
合规要求通常 6 个月到 1 年。分冷热层:最近 30 天热查询(Elasticsearch),历史归档冷存储(S3/OSS + 压缩)。溯源时多数只需要最近几天,别为「1 年前某条日志」花大价钱买热存储。
-
如何检测内网横向移动?
这是最难的。关键指标:异常 SMB/RDP 连接(工作站之间)、新服务创建、权限提升(普通用户突然有管理员权限)、大量文件访问。EDR 比网络 IDS 更有效。
-
日志里能不能记密码?
绝对不行。password、token、secret 字段必须在日志输出前脱敏。Spring 里用 @JsonIgnore 或自定义 Logback Pattern 过滤。
错法对照
| 错法 | 问题 | 正解 |
|---|
| 只采集错误日志 | 无法溯源正常操作 | INFO 级别安全事件全采集 |
| 日志写本地不收集 | 服务器被攻破后日志被删 | Filebeat 实时外发 |
| 审计日志应用账号可删 | 攻击者抹痕迹 | 只追加,独立账号,定期异地备份 |
| 告警规则「登录成功」 | 每天几千条,全部忽略 | 只告警异常模式 |
| 日志里有明文密码 | 日志库=密码库 | 脱敏+访问控制 |
| 保留 7 天日志 | 入侵数月后发现无法溯源 | 合规要求 180 天+ |
卡点急救
-
Filebeat 不采集新日志:检查 Filebeat 进程是否运行、filebeat.yml 路径是否正确、registry 文件是否记录了错误的 offset(删除 /var/lib/filebeat/registry 后重启)。用 filebeat test output 验证 ES 连接。
-
Elasticsearch 磁盘爆了:配置 ILM(Index Lifecycle Management)自动删除老索引。30 天热数据 + 180 天冷数据,过期自动删。别等磁盘满再处理。
-
告警太多不知道哪些是真的:每条告警加「置信度评分」——多个弱信号组合成强信号(失败 5 次 + 新地理位置 + 非工作时间 = 高置信)。单独一条「失败 1 次」不告警。持续调优,目标是每天告警 < 10 条且每条都需要行动。
面试常问
-
SIEM 的核心功能是什么?
三个:① 日志聚合——从防火墙、IDS、应用、主机等多源收集日志到统一平台;② 关联分析——跨源关联事件(如「防火墙拦截 + 登录失败 + 新文件创建」= 入侵尝试);③ 告警与响应——基于规则触发告警,部分支持自动化响应。价值在于「从噪音中发现信号」。
-
IDS 和 IPS 的区别?
IDS(入侵检测系统):旁路部署,镜像流量分析,只报警不阻断,对网络性能无影响,误报容忍度高。IPS(入侵防御系统):在线部署,串联在流量路径上,实时阻断恶意流量,但误报会误伤正常业务,性能要求高。边界用 IPS(WAF),内部用 IDS(EDR)。
-
如何设计一个有效的安全监控规则?
① 高信号低噪音:规则必须指向明确的攻击模式(如「5 次失败登录」而非「1 次失败」);② 上下文关联:结合用户历史行为、IP 信誉、时间异常;③ 可行动:每条告警必须有明确的响应步骤(封 IP/改密/人工排查);④ 定期调优:误报率 >20% 的规则要么改进要么下线。
自测三问
- 为什么「登录成功」不是一个好的告警规则?什么组合才是?
- 审计日志为什么要「只追加」?如果应用账号能删日志会怎样?
- 本站必须采集的 5 类日志是什么?各自检测什么攻击?
小结一句话
检测的本质是承认防御会失败:SIEM 聚合日志发现异常模式、EDR 监控主机行为、告警必须可行动——目标是发现入侵的时间从「月」缩短到「分钟」。
延伸