漏洞修复慢几乎是每个开发团队都绕不开的痛点。扫描工具早就装了,漏洞报告也定期生成,可真到了要修的时候,邮件石沉大海、工单无人认领、修复排期一拖再拖,最后漏洞在系统里一躺就是几个月。其实大多数团队的短板并不在扫描环节,而在扫描之后的通知、分派和跟踪环节。本文就来聊聊如何用自动化扫描加上一套靠谱的通知机制,把漏洞修复这件事真正管起来。

漏洞修复慢的根因在哪里
先别急着上工具,得搞清楚慢在哪里。复盘大多数团队的漏洞处理流程,会发现几个共性问题。第一是信息断层:扫描结果发到安全团队,安全团队再转发给开发负责人,开发负责人再分给具体的开发人员,中间任何一环掉链子,漏洞就没人管了。第二是优先级缺失:一份报告里塞了几百个漏洞,从高危到低危混在一起,开发人员根本不知道先修哪个,索性全都往后放。第三是没有闭环:漏洞通知出去了,但没人跟踪有没有修、多久修完,缺少度量和问责。
这三个问题的解法其实是一体的:用自动化扫描保证发现的及时性和一致性,用分级通知保证信息精准触达到责任人,用闭环跟踪保证修复有始有终。后面两节分别展开。
搭建自动化扫描流水线
自动化扫描的核心思路是把扫描嵌入到开发流程中,而不是靠安全团队定期手动跑一遍。最典型的接入点是CI/CD流水线:代码提交时扫描依赖,镜像构建后扫描镜像,部署前做一次最终校验。这样漏洞在进入生产环境之前就被拦截,而不是事后补救。
依赖扫描推荐使用OWASP Dependency-Check或者Trivy的文件系统扫描模式,镜像扫描则首选Trivy,它的扫描速度和数据库更新都比较理想。下面是一个在GitLab CI中使用Trivy扫描容器镜像的配置示例:
stages:
- build
- scan
trivy-scan:
stage: scan
image: aquasec/trivy:latest
script:
# 扫描构建出来的镜像并输出JSON报告
- trivy image --format json --output trivy-report.json $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
# 高危及以上漏洞直接让流水线失败,强制修复
- trivy image --exit-code 1 --severity HIGH,CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
artifacts:
paths:
- trivy-report.json
when: always
这个配置的关键在于--exit-code 1这一行:只要存在高危及以上漏洞,流水线就直接失败,开发人员想忽略都忽略不了。当然,如果一下子修不完,也可以先用--ignorefile机制建立豁免清单,给每个豁免项设置有效期,避免一豁免就是永久。有些团队担心扫描拖慢流水线,可以把Trivy数据库缓存到流水线缓存目录中,扫描一个中等规模镜像通常在三十秒到一分钟内完成,完全可以接受。
除了CI阶段,还应该保留一个定时全量扫描任务,比如每天凌晨对生产环境所有运行中的镜像和依赖做一次扫描。增量扫描管住新增代码,全量扫描兜底存量问题,两者缺一不可。
分级通知机制的设计与实现
扫描结果出来之后,怎么通知直接决定修复速度。实践中比较有效的做法是漏洞分级加上渠道分级。高危和严重级别的漏洞,直接推送到企业微信或钉钉群并@具体责任人,同时自动创建带截止时间的工单;中低危漏洞则汇总成日报或周报,避免信息轰炸导致大家麻木。
下面用一个Python脚本来演示如何解析Trivy的JSON报告,并根据严重级别选择不同的通知渠道。高危漏洞推送到钉钉机器人,中低危汇总成邮件:
import json
import requests
def load_report(path):
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
def send_dingtalk(webhook, text):
# 高危漏洞直接推送到钉钉群并提醒责任人
requests.post(webhook, json={
'msgtype': 'text',
'text': {'content': text},
'at': {'isAtAll': True}
}, timeout=10)
def build_message(report):
critical, high, others = [], [], []
for result in report.get('Results', []):
for vul in result.get('Vulnerabilities') or []:
sev = vul.get('Severity')
item = f"{vul['VulnerabilityID']} {vul['PkgName']} {sev}"
if sev == 'CRITICAL':
critical.append(item)
elif sev == 'HIGH':
high.append(item)
else:
others.append(item)
return critical, high, others
report = load_report('trivy-report.json')
critical, high, others = build_message(report)
if critical or high:
msg = '【紧急】发现高危漏洞,请立即处理\n' + '\n'.join(critical + high)
send_dingtalk('https://oapi.dingtalk.com/robot/send?access_token=xxx', msg)
# 中低危走邮件汇总,代码略
通知内容的设计也有讲究。一条有效的漏洞通知至少要包含:漏洞编号和严重级别、影响的组件和版本、影响的系统或服务、修复建议(升级到哪个版本)、以及明确的修复截止时间。缺了截止时间的通知基本等于没发,因为没有人会觉得这件事紧急。建议高危漏洞给四十八小时,严重漏洞给二十四小时,中低危跟随正常迭代排期。
另外要注意通知的可配置性。不同项目可能有不同的责任人和值班群,把这些映射关系放在配置中心或数据库里,脚本按项目维度查表分发,而不是硬编码在代码中,后续维护成本会低很多。
建立修复闭环与度量指标
通知发出去了不等于流程结束,还需要闭环。最简单可行的闭环方式是把漏洞修复状态集中管理:扫描系统发现漏洞后自动登记,修复完成后由流水线里的复扫自动核销,超期未修复的漏洞自动升级通知到上一级负责人。这样整个链路不需要人工维护台账,状态始终是最新的。
度量方面,建议关注两个核心指标:平均修复时间(MTTR)和漏洞超期率。平均修复时间按严重级别分别统计,比如高危漏洞平均多久修完、超期率是多少,每个迭代回顾一次。有了数据之后,修复慢的团队和环节会非常直观地暴露出来,改进也就有了抓手。
最后提醒一点:工具再完善也只是手段,漏洞修复最终还是要落到责任和流程上。自动化扫描解决发现和提醒的问题,分级通知解决触达的问题,闭环度量解决督促的问题,三者配合起来,把漏洞平均修复时间从几周压缩到几天是完全 realistic 的目标,不少践行DevSecOps的团队已经验证了这条路。