PLFM_RADAR是我们在做平台化安全能力时落下的一个内部项目,名字里的PLFM是Platform,RADAR则是个比喻——它不对空域探测,而是对互联网资产做持续测绘和风险预警。项目的最初形态挺朴素:一台服务器、一张定时任务表、一个Nginx反向代理,但跑了大半年之后,它成了安全团队每天打开的第一块屏幕。这篇文章把PLFM_RADAR从立项到上线的完整链路写清楚:它解决什么问题、核心模块怎么设计、指纹识别和风险评分怎么落地,以及那些在文档里绝对不会写的坑。适合正在搭资产测绘或攻击面管理平台的工程师,也适合想知道"安全团队到底每天在看什么"的产品和运维同学。
1. 一次凌晨的漏洞通告,把"家底不清"的问题彻底摆上了台面
事情是从一封凌晨的漏洞通告邮件开始的。第三方漏洞平台发来告警,标题写着"某测试环境存在未授权访问漏洞,危害等级:高",要求24小时内完成修复。值班同事爬起来排查,发现这台机器是三个月前上线的一个内部系统测试机,部署完成后负责人换了三轮,连系统归属部门都没人说得清。更麻烦的是,这个环境用了临时域名和随机端口,重新梳理网络拓扑才定位到物理位置。
这个场景很多安全团队都经历过。真正的痛点不是"漏洞被曝光",而是"我们不知道这台设备存在"。传统安全建设习惯把精力放在WAF、HIDS、防火墙这些防护设备上,默认前提是"我已经知道自己有哪些系统"。但现实是:业务发展快的时候,开发为了赶进度会临时起一台服务器,运维为了联调会开放一个额外端口,甚至连云账号开了新的VPC都未必同步给安全团队。这些节点在防护体系之外,却在公网上真实暴露着。
1.1 传统资产台账为什么救不了场
我见过不少团队维护资产台账的方式:一张Excel表格,字段包括资产名、IP、负责人、上线时间。刚维护的前三个月是准的,之后就逐渐失真——新系统上线没人填,老系统下线没人删,负责人调动后表格里还是旧名字。安全团队拿着这份台账做基线检查,等于拿着错误地图找路。
PLFM_RADAR立项时的第一个动作,就是把这套思路反过来:不依赖人工录入,而是让系统自己去"看"。从已确认的少量种子资产出发,通过域名解析、IP段扫描、证书透明日志、外部威胁情报等渠道,把暴露在互联网上的资产一点点画出来,再回填责任人信息。人工台账变成校验参考,而不是数据源。
1.2 三个目标决定项目边界
第1章写多了容易变成需求讨论。当时我们把PLFM_RADAR的交付目标收敛成三句话:
- 持续测绘:周期性发现并更新域名、IP、端口、服务,形成动态资产清单。
- 自动识别:通过指纹技术判断每个服务是什么系统、什么版本、属于哪个业务。
- 风险排序:结合漏洞信息、暴露程度和资产重要性,给出可量化的风险分。
这三个目标直接决定了后面的架构选择。我们不打算做一个"漏洞扫描器",因为市面上已经有很成熟的Nessus、AWVS;也不打算做一个"合规报表系统",因为报告再漂亮也不能解决"不知道资产在哪"的根本问题。PLFM_RADAR的定位更接近"资产底座"——它把最基础的盘点工作自动化,然后把数据开放给其他安全系统调用。
1.3 为什么叫RADAR而不叫"资产扫描器"
命名上我们有过讨论。团队里有人提议叫"资产发现平台",太绕。后来一致同意用RADAR,是因为雷达的工作逻辑和资产测绘很接近:雷达主动发射波束,接收回波,判断目标方位和性质;PLFM_RADAR主动发起探测请求,分析响应特征,判断目标服务和风险状态。雷达不会因为某个方向"看起来安全"就停止扫描,这正是我们需要的工作状态。
2. 画资产、认指纹、定风险:PLFM_RADAR的核心闭环
项目启动时,工程文档里画了三张流程图,后来被我们并成一条闭环:采集原始数据,提炼资产主体,识别服务指纹,汇总风险评分。这一章把这条链路拆开讲。
2.1 画资产:从域名、IP到服务实例
所谓"画资产",不是简单拉一个域名清单,而是把资产之间的关系建立起来。PLFM_RADAR里最重要的资产实体有三个:
| 实体 | 关键字段 | 说明 |
|---|---|---|
| 域名 | domain, cname, mx, ns, 解析IP列表 | 资产入口 |
| IP资产 | ip, port, protocol, 归属ASN/地区 | 网络位置 |
| 服务实例 | host:port, 指纹标签, 指纹版本, 首次发现时间 | 真正被攻击的客体 |
阶段一的数据来源,是内部DNS解析日志和已有CDN配置。我们把已知域名丢进去做被动DNS解析,拿到一批IP。然后用主动扫描去探测这些IP的开放端口,再对端口做服务识别。第一次全量扫描跑了差不多两天,产出的结果让我们吃了一惊:发现的实际端口数是原台账记录的三倍多,其中Redis、MongoDB、MySQL这类数据库端口直接暴露在公网的有十几个。
画资产这个阶段最容易被低估的是"归一化"工作。同一个系统可能同时有A记录、CNAME、多个端口,如果不去重,资产数量表会出现大量噪声;如果去重过度,又会把真实独立节点合并掉。我们的做法是:以IP:Port作为服务实例的唯一标识,同时保留域名到IP的多对多关系,靠后续的指纹关联来判断哪些服务实例属于同一套系统。
2.2 认指纹:让机器回答"这是个什么东西"
光知道"IP 1.2.3.4的443端口开了"没有意义,攻击者利用的是具体产品的漏洞。所以第二个阶段要让机器回答:这个端口上跑的是Nginx还是Apache?是Spring Boot还是ThinkPHP?是门户网站还是管理后台?答案由指纹识别引擎给出。
指纹识别的本质,是用一组"特征规则"去匹配"探测响应数据"。规则来源包括:
- HTTP响应头特征:如
Server: nginx/1.18.0,X-Powered-By: PHP/7.2。 - 页面内容特征:HTML里的关键词、meta生成器标签、固定静态资源路径。
- 行为路径特征:访问某些固定URL是否返回特定内容,例如
/wp-login.php暴露WordPress。 - TLS证书特征:证书的CN、SAN、组织名、扩展字段。
- 特殊文件哈希:如favicon.ico的MD5,很多框架自带独特的favicon。
PLFM_RADAR把几十种常见中间件、框架、管理后台的指纹整理成了规则库,格式类似这样:
- name: "Nginx" category: "web-server" priority: 80 matches: - type: header field: Server regex: "nginx/([0-9.]+)" confidence: 0.9 - type: banner regex: "^nginx$" confidence: 0.7每条规则都带一个置信度权重,服务会收集到若干条命中结果,最终得分就是加权后的最大值。现在这套规则库维护了一年多,覆盖了近两百个常见产品和组件,识别准确率大概稳定在九成左右,剩下的误差大多出现在老版本改版和自定义开发场景。
2.3 定风险:把"可能有问题"量化成可处置的分数
识别出服务版本之后,就可以判断"有没有已知漏洞"了。但这里有个很容易犯的错误:把CVSS分数直接当成处置优先级。CVSS只描述漏洞本身的严重程度,没有考虑资产在业务里的角色。一个边缘测试系统上的高危漏洞,和一个核心生产库上的中危漏洞,直接按CVSS排序会让团队天天救火。
PLFM_RADAR的风险评分做了二次加权,前面的章节会详细讲公式,这里先给出结论:定风险的本质是组合判断,资产重要性、暴露程度、漏洞真实可利用性、是否被外部威胁情报标记,四个维度叠起来才是可执行的优先级。
3. 系统架构与核心模块拆解
PLFM_RADAR从架构上看并没有特别高深的东西,但它把很多通用组件组合成了一个完整闭环。整个系统分为四层:采集层、存储层、分析层、展示层。我重点讲前三层,因为绝大多数工程难点都堆在这里。
3.1 采集层:主动测绘和被动监听两条腿走路
采集层是PLFM_RADAR的信息来源,分主动和被动两路。
主动采集是主力,流程是:
- 目标生成:从资产库取出需要扫描的IP、域名和端口范围。
- 端口预探测:用masscan做全端口SYN探测,快速筛选出开放端口。
- 服务识别:对开放端口执行Nmap的服务版本识别和httpx的Web指纹探测。
- 指纹采集:拉取页面、响应头、证书、favicon等原始数据存入暂存区。
被动采集是补充,主要接内部DNS日志和主机流量元数据。它的价值在于能发现主动扫描永远发现不了的东西:内网DNS里出现了一个新域名,流量日志里出现了一个外联IP,这些信息能帮助我们发现"种子资产"之外的未知边界。
被动数据有个好处,频率高、成本低,适合做实时感知;但也有个毛病,噪声大,需要大量清洗。比如内网DNS里有大量healthcheck、autodiscover这种随机探测记录,如果不做过滤,资产库会迅速膨胀。
3.2 存储层:资产数据为什么不能只放一种数据库
这个选择我们踩过坑,一开始图省事把所有数据塞进MySQL,结果扫描结果一多,单表轻松破亿,查询慢到没法用。后来重新设计了存储方案:
| 用途 | 存储引擎 | 设计要点 |
|---|---|---|
| 资产关系(域名、IP、归属) | PostgreSQL | 主库,强事务,维护资产主体 |
| 扫描记录与指纹原始数据 | Elasticsearch | 全文检索,按天建索引,定期冷数据迁移 |
| 任务队列和去重状态 | Redis | 短生命周期,高吞吐 |
| 统计报表、趋势分析 | ClickHouse | 列存,适合多维度聚合 |
PostgreSQL和Elasticsearch之间通过资产ID关联,这里有个细节:ES里只存"事实记录",比如某次扫描发现某端口返回什么指纹;而每个资产的最新状体态放在PostgreSQL里。这样既保证历史可追溯,又让查询快速。
3.3 分析层:标签、关联和沉睡资产发现
分析层负责把采集到的原始数据"翻译"成安全团队能理解的信息。
第一步是打标签。标签体系分为几类:技术标签(语言、框架、中间件)、业务标签(生产、测试、管理后台)、异常标签(公网暴露、弱口令、敏感端口)。标签由规则引擎、指纹结果和人工运营共同产出。
第二步是关联。一个开放了3306端口的IP,同时指纹结果是MySQL,并且在证书或历史DNS里能关联到某个域名,那它大概率是数据库服务。这种关联让资产画像从"一列IP"变成"一套系统的节点"。
第三步是沉睡资产发现。定义很简单:超过30天没有活跃流量、没有更新记录、但依然暴露在公网上的资产。这些节点往往是安全团队最大的盲区,也是攻击者最喜欢的目标。PLFM_RADAR每天跑一次沉睡资产检测,把命中列表推送给人处理。
3.4 任务调度:一次全量扫描怎么拆成百万级小任务
PLFM_RADAR刚上线时的扫描任务调度,就是一个简单的定时脚本按顺序跑,结果经常出现某台采集机扫到一半卡住,半个流程全停摆。后来改成基于Redis队列的调度模型:
- 每次全量扫描任务被拆成
目标IP段 + 目标端口段的原子任务,每个任务处理一小块范围。 - 工作节点从队列里取任务,执行完成后上报结果,按
指数退避策略做重试。 - 每个IP段的任务执行完会打一个标记,下次调度时跳过已完成段,支持增量扫描。
这个模型并没有用很重的框架,Redis + 一个简单的Python Worker进程就搞定了。拆小任务的好处除了容灾,还能精准分配扫描速率,避免一下子把出口带宽打满。
4. 指纹识别引擎的工程实现细节
指纹识别是PLFM_RADAR里技术含量最集中、也最体现"做没做过"的部分。市面上很多扫描器也能出指纹,但真正落地到"能持续维护、能控制误报、能和漏洞信息联动"的并不多。
4.1 一次探测请求的完整生命周期
拿最常见的Web服务识别的链路举例:
- 采集器发起HTTP请求,带上一个模拟浏览器的User-Agent,设置5秒超时。
- 接收响应后原始数据会被切成四个部分:状态码、响应头、页面正文(截取前256KB)、TLS证书。
- 分别对这四个部分做MD5哈希,把哈希值和原始数据一起落地到对象存储,方便后续规则变更时离线重算。
- 指纹引擎读取规则库,对采集到的数据执行匹配。
真实请求可能会遇到网络抖动、协议形态差异,比如WebSocket服务对普通HTTP请求返回的是非标准响应。所以在请求阶段会设置"重试一次 + 一次指纹探测请求失败后改用HTTPS再探测"的逻辑。这个细节保证了指纹识别的召回率。
4.2 指纹匹配:从精确匹配到置信度打分
指纹匹配不复杂,但需要设计一套置信度机制。我们把匹配方式分成四档:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| 精确匹配 | 哈希、状态码、固定URL | favicon识别、框架固定资源 |
| 含匹配 | 响应头、正文中是否包含某字符串 | 版本号、X-Powered-By、特定Cookie |
| 正则匹配 | 用正则表达式提取版本信息 | 从Server头、JS文件里提取版本 |
| 组合规则 | 多条子规则同时满足 | 降低泛匹配误报 |
每条规则命中后得到confidence值,多个规则命中同一组件时,最终分数取加权最大值。比如Server: nginx命中的置信度是0.9,如果同时页面里检测到/nginx-logo.png,再加0.05,但总分封顶1.0。
置信度分还有一个用途:决定是否需要主动验证。当一条指纹的置信度在0.5到0.75之间时,系统会发起一次补充探测,比如访问特征URL、拉取特定目录列表,来确认或推翻当前判断。
4.3 误报处理:泛匹配、同源指纹与版本识别
指纹误报的来源主要有三类:
泛匹配:某些规则写得太宽,比如body里匹配"powered by"就认为某个CMS,实际上这句话可能出现在任何页面的footer里。解决方法是加"否定规则",出现过某个字符串就排除。
同源指纹:多个产品共用同一套开源代码或模板,导致指纹撞车。最典型的例子是Discuz的模板被很多地方门户二次开发,favicon和部分路径完全相同。这种只能靠更细粒度的特征来区分,比如特定函数名、特有接口路径、cookie结构。
版本识别:有些指纹能识别出"是Nginx",但拿不准具体子版本。PLFM_RADAR的处理方式是:版本信息单独存字段,风险评分时如果子版本未知,按"未知版本"而不是"无漏洞"处理,提醒人工复核。
4.4 指纹规则库的维护节奏
指纹库不是一锤子买卖,它需要跟着新框架、新版本持续更新。我们的维护节奏是一个月两次,流程是:
- 从外部威胁情报和GitHub release里收集新出现的组件。
- 搭建一个临时环境安装该组件,采集真实响应数据,归纳特征。
- 把规则提交到测试库,跑一遍历史数据,看是否产生大量误报。
- 通过后发布到线上生产规则库。
规则维护还必须配一个样本库:把每次扫描中"识别失败但被人工确认为某组件"的样本收集起来,作为下次规则优化的输入。这是PLFM_RADAR能越用越准的关键。
5. 风险评分与告警降噪:让平台不会变成"狼来了"机器
系统刚能跑之后,安全团队收到了一堆告警,两周后大家开始麻木。因为告警太多了,真正严重的问题淹没在海量噪声里。这一章讲评分模型和降噪手段。
5.1 为什么告警越做越多,处置却越来越慢
告警多的根源,是检测规则缺少上下文。比如"公网暴露了SSH服务"本身不算漏洞,但如果这个IP还挂着管理后台指纹、近30天有来自境外的登录爆破流量、资产重要性又很高,那它才是真正需要立即处置的风险。PLFM_RADAR把告警从"一条规则命中"升级为"多维证据组合",让平台输出的每一条告警都能直接对应到处置动作。
5.2 评分公式:资产重要性、暴露程度、漏洞确证、威胁情报
最终的风险分采用加权公式,四个维度如下:
risk_score = 0.30 * asset_importance 0.25 * exposure_level 0.30 * vuln_confirmed_factor 0.15 * threat_intel_factor- asset_importance(资产重要性):按域名资历、业务属性、是否涉及敏感数据等指标打分,0~10分。
- exposure_level(暴露程度):端口暴露范围、是否绕过认证、访问来源是否覆盖全网,0~10分。
- vuln_confirmed_factor(漏洞确证因子):有没有匹配到已知CVE/漏洞库,CVSS有多高,0~10分。
- threat_intel_factor(威胁情报因子):该资产IP/域名是否出现在恶意IP库、僵尸网络C&C情报里,0~10分。
最后用risk_score >= 7作为"必须人工处置"的阈值。资产重要性分数由业务团队在平台上维护,我们一开始用域名是否带.com这种粗粒度规则去猜,后来才发现人工维护才是性价比最高的方式。
5.3 降噪三板斧:收敛、白名单、时间窗合并
即使有了评分,平台还是会有重复和低价值的告警。我们用了三个操作把告警量砍掉大约七成:
**第一斧:收敛。**同类资产、同一天发出相同告警,只保留一个主告警,其余作为附件详情。比如同一个IP段的100台机器都暴露了同一个高风险端口,合成一条"该IP段100台机器批量暴露"。
**第二斧:白名单。**有一些资产是业务主动暴露的,比如对外开放的官网、客户回调接口。这些资产业务方明确确认过安全策略,就在白名单里备注原因,告警自动降级为低优先级。
**第三斧:时间窗合并。**同一个资产的同一类告警,在24小时窗口内只提醒一次,如果处置超时再升级。这一条直接减少了"早上9点重复推送前晚告警"的问题。
5.4 处置闭环:工单、复测打分与止损确认
PLFM_RADAR不是只发一个告警就结束了。平台对接了内部缺陷管理系统,告警达到阈值后自动创建工单,附带资产画像、指纹信息、风险标记和修复建议。处置完成后,工单系统回传"已修复"状态,平台会自动发起一次复测,验证端口是否已关闭、受影响组件是否已升级。
复测通过的工单,风险评分会降低并进入观察期;复测没通过的,工单会自动重新打开并且提示"修复措施未生效"。这套闭环跑起来之后,团队才真正敢说"所有高风险问题都在处置中"。
6. 落地过程中踩过的坑和工程调整
技术方案讲起来条理清晰,落地过程却是一路踩坑踩过来的。这章把几个印象深刻的坑分享出来,给正在做类似平台的同学一点前车之鉴。
6.1 第一个坑:把扫描频率调成一小时一次,交换机先扛不住了
一开始为了追求"实时",我们把资产扫描频率设成了每小时一次。跑了两天,网络团队找上门,说出口交换机CPU长时间高负载,部分办公网络已经出现丢包。排查定位到是masscan的高速率SYN探测把交换机的转发队列打爆了。
后来调整了策略:高频小范围扫描与低频全量扫描分离。每天跑一次全量端口探测,每两小时只扫「已发现开放端口」做存活确认,真正的高频监测放在被动数据收集上。这个调整让扫描峰值速率下降了80%,资产新鲜度却没有明显下降。
6.2 第二个坑:泛域名把资产库撑爆,还拖垮了评分
被动DNS日志接入后,资产库的域名数量一夜之间翻了几十倍。仔细一看,大部分是web-xxxx.internal.example.com这类泛域名自动生成记录。这些域名不是真实资产,但评分体系会把它当成独立资产打高分,导致大量无意义的告警。
处理方式是给资产库加了一个"泛域名聚簇"能力:域名按主域名分组,如果某个主域名下动态子域名超过一定数量,就自动归为"动态域名簇",不再逐条告警,只对簇内出现真实Web服务指纹的个别域名单独关注。
6.3 第三个坑:指纹规则写得太激进,把企业网关认成路由器
有一次平台把公司出口网关识别成了某品牌路由器,并附带了一个中危漏洞告警。排查下来,是因为网关设备的管理页面使用了某开源项目的前端模板,而那条指纹规则刚好匹配了模板特征。这个坑让我们意识到,指纹规则不能只看"响应当中出现什么",还要看"排除性特征"。后来的规则库每个条目都增加了exclude字段,比如匹配到某特定路径就直接排除,避免单一特征误判。
6.4 边界感:扫描授权的刚性规定和一点个人体会
PLFM_RADAR做的是主动探测,所以授权边界从一开始就是刚性的。我们对所有扫描目标做分类:属于自有资产的范围正常扫描;属于云上租户的资源需要确认云平台租约合同允许安全测试;属于第三方但在业务链上的系统,只做被动数据关联,绝对不发起主动扫描。每季度也会重新梳理一次目标清单,防止业务下线后的资产被漏出扫描范围。
如果让我重新做一次PLFM_RADAR,我会把两件事提前:第一是提前设计好资产白名单的运营流程,而不是等技术上线后才让业务团队补信息;第二是做一套完整的指纹样本库,从第一天就开始积累,后面优化规则会省掉大量回看历史数据的时间。整个项目做下来,最深的感觉是——安全平台的复杂性不在某个算法里,而在那些每天和数据噪声、误报、资产边界较劲的细节里。把这些细节处理好了,平台才能真正成为团队信任的雷达。