第三章:资产发现与清单管理

说实话,做供应链安全这几年,我见过太多团队栽在同一个坑里——连自己有什么资产都没搞清楚,就开始搞漏洞排查。这就像你连家里有多少扇窗户都不知道,就急着要装防盗网,你说荒唐不荒唐?

我个人习惯,做任何安全项目之前,第一件事就是摸清家底。今天我们就聊聊,怎么自动发现供应链里的那些资产,以及怎么维护这份清单。

3.1 资产发现:你到底有哪些家底?

供应链里的资产,说白了就四类:IP、域名、代码仓库、第三方组件。每一类都有它自己的脾气,得用不同的法子去发现。

3.1.1 IP 资产发现

IP 这东西,最容易被忽略。很多公司只记得自己的公网 IP,内网段、云上 VPC 里的 IP 全忘了。我在项目中遇到过一家公司,他们以为自己只有 10 个公网 IP,结果我一扫,发现还有 30 多个没人管的 IP 挂在云上,其中一台还跑着旧版本的 OpenSSH。

常用的自动发现手段:

  • DNS 解析记录:从你的 DNS 服务器里导出所有 A 记录和 CNAME 记录
  • CDN 日志:从 CDN 日志里提取源站 IP
  • 云服务商 API:调用 AWS/GCP/Azure 的 API,拉取所有实例的 IP
  • 网络扫描:用 Nmap 或 Masscan 扫描已知网段
我的小技巧:别只扫 80/443 端口。很多内部服务跑在 8080、8443、3000 这些端口上,漏掉一个可能就是个大窟窿。

3.1.2 域名资产发现

域名比 IP 更麻烦。为什么?因为域名可以无限嵌套子域名。你想想看,一个主域名下面可能挂着几百个子域名,有些子域名甚至没人记得是谁创建的。

我记得有一次帮客户做排查,他们只提供了 5 个主域名。我用工具一跑,发现关联了 200 多个子域名,其中有个子域名指向一个测试环境,上面跑着生产数据库的备份。嗯,这种事儿真不少见。

自动发现域名的常用方法:

  • 证书透明度日志:从 crt.sh 拉取所有关联域名
  • 搜索引擎:用 Google/Bing 的 site: 语法
  • DNS 枚举:用 dnsrecon 或 subfinder 暴力枚举子域名
  • 被动 DNS:从 SecurityTrails 等平台获取历史 DNS 记录
# 一个简单的子域名发现脚本示例
#!/bin/bash
DOMAIN="example.com"
# 从 crt.sh 拉取证书日志
curl -s "https://crt.sh/?q=%25.$DOMAIN&output=json" | \
  jq -r '.[].name_value' | \
  sort -u > subdomains.txt
echo "发现 $(wc -l < subdomains.txt) 个子域名"

3.1.3 代码仓库发现

代码仓库这块,水更深。大公司可能有几百个 Git 仓库,有些是公开的,有些是私有的,还有些是员工自己建的。我曾经见过一个案例,某员工把自己的 GitHub 仓库设成了公开,里面居然写着数据库连接密码。

自动发现代码仓库的方法:

  • GitHub/GitLab API:通过组织名拉取所有仓库列表
  • 代码扫描工具:用 GitLeaks 或 TruffleHog 扫描公开仓库
  • CI/CD 日志:从 Jenkins/GitLab CI 日志里提取仓库地址
  • 依赖文件分析:从 package.json、requirements.txt 里反向推断仓库
注意:别只盯着官方仓库。Fork 出去的仓库、离职员工个人账号下的仓库,都是潜在的风险点。我建议定期扫描 GitHub 上所有包含你公司关键字的仓库。

3.1.4 第三方组件发现

第三方组件,说白了就是那些你从外面拿来的代码。npm 包、Maven 依赖、PyPI 包、Docker 镜像……这些东西你用得爽,但风险也最大。一个 Log4j 事件,全球多少公司中招?

自动发现第三方组件的方法:

组件类型 发现方法 常用工具
JavaScript 包 解析 package-lock.json npm audit, Snyk
Java 包 解析 pom.xml 或 build.gradle OWASP Dependency-Check
Python 包 解析 requirements.txt 或 Pipfile.lock pip-audit, Safety
Docker 镜像 扫描 Dockerfile 或镜像层 Trivy, Clair
Go 模块 解析 go.mod 和 go.sum govulncheck

3.2 资产清单维护:别让清单变成废纸

发现资产只是第一步。更难的是怎么维护这份清单。我见过太多团队,花了两周时间整理出一份资产清单,然后就扔在共享文件夹里吃灰。三个月后,清单上的信息已经过时了,还不如没有。

为什么会这样?因为资产是动态的。新服务上线、旧服务下线、IP 变更、域名新增……每天都在变。你想想看,如果清单不跟着变,那它还有什么用?

3.2.1 自动化更新策略

我个人建议,资产清单的更新必须自动化。手动更新?不存在的。人都会偷懒,都会忘记。

  • 定时扫描:每天凌晨跑一次资产发现脚本,自动比对差异
  • 事件驱动:监听云服务商的 EventBridge 或 Webhook,有新资源创建时自动更新
  • CI/CD 集成:在流水线里加入资产注册步骤,每次部署自动更新清单
# 一个简单的资产差异比对脚本
#!/bin/bash
# 获取当前资产列表
curl -s "https://api.example.com/assets" | jq '.' > current_assets.json
# 与上次的清单比对
diff -q current_assets.json last_assets.json
if [ $? -ne 0 ]; then
  echo "资产清单有变化!"
  # 发送通知
  curl -X POST "https://hooks.slack.com/..." \
    -d '{"text":"资产清单已更新"}'
  # 保存当前清单
  cp current_assets.json last_assets.json
fi

3.2.2 资产生命周期管理

资产不是永生的。它也有生命周期:创建、运行、下线、销毁。很多公司只关注前两个阶段,忽略了后两个。结果就是,一堆已经下线的资产还挂在清单上,浪费存储空间不说,还容易误导人。

我建议给每个资产打上状态标签:

  • active:正常运行中
  • deprecated:已废弃,但还未下线
  • decommissioned:已下线,保留记录
  • unknown:状态不明,需要人工确认
关键点:对于状态为 unknown 的资产,必须设置一个自动提醒。如果超过 30 天无人认领,自动标记为 deprecated,再等 30 天自动转为 decommissioned。这样既不会误删,也不会让僵尸资产一直占着位置。

3.2.3 资产归属与责任人

每项资产都必须有明确的责任人。没有责任人的资产,就是没人管的孤儿。我曾经处理过一个事故,一个内部 API 被攻击了,结果查了半天,没人知道这个 API 是谁部署的。最后发现是某个离职员工三年前做的实验项目。

所以,资产清单里必须包含:

  • 负责人:当前谁在维护
  • 创建人:谁创建的
  • 业务线:属于哪个部门
  • 联系方式:紧急联系人
  • 最后更新时间:上次确认的时间

3.3 避坑指南

做了这么多年供应链安全,我踩过的坑不少。分享几个典型的:

我曾经犯过的错:刚开始做资产发现时,我只关注了生产环境,忽略了测试环境和预发布环境。结果有一次,测试环境被入侵,攻击者通过测试环境跳到了生产环境。从那以后,我把所有环境都纳入了资产清单。

还有一个坑:别太相信自动发现工具。工具只能发现它知道的东西。有些资产,比如内部自建的代码仓库、老旧的 FTP 服务器,工具可能扫不到。所以,自动发现 + 人工确认,才是王道。

嗯,最后说一句:资产清单不是做给别人看的,是给你自己用的。别为了应付审计而做清单,要为了真正管理风险而做。这个心态不摆正,后面所有的工作都是白搭。