尧图精选

SBOM落地实战:从格式选型到CI/CD集成,构建软件供应链透明化

🕒 发布时间:2026/10/1 5:36:42 📁 来源:尧图网络
1. 想清楚再动手SBOM到底解决什么问题先聊点实在的。这里说的物料清单SBOMSoftware Bill of Materials不是工厂里那种纸质或SAP界面里维护的生产物料清单而是针对软件工程的一件“成分标签”。我第一次接触SBOM时第一反应是“这不就是packages清单吗”。当时手头有个项目被客户要求交付一份“软件物料清单”客户那边的安全团队拿了一份模板要列清楚每个开源组件、版本号、许可证、漏洞情况。我一开始还真就导出了package.json往文档里一贴结果对方直接打回来说“这不是SBOM我们要的是标准格式、机器可读、能自动进漏洞扫描流程的东西”。从那以后我才认真去研究SBOM到底是个什么东西也踩了不少坑。先说清楚一个问题SBOM的价值到底是什么简单说就是解决软件供应链的透明度。你的应用里用了哪些开源库这些库又依赖了哪些子库中间嵌套了好几层如果有个组件被爆出高危漏洞你能不能快速定位“我的服务受不受影响”代码是你写的依赖是别人写的你不可能把所有组件都读一遍源码SBOM就是在你、你的组件、你的客户之间建立起一张可以自动对照的清单。另一个很现实的驱动力是合规。这两年国内外的安全合规要求越来越多很多政务、金融、运营商项目在验收时都会被问到软件供应链安全。你要是拿不出一份像样的SBOM甚至可能连上线审批都过不了。还有一些项目是给客户做交付的客户有自己的SCA软件成分分析平台你给出SBOM他们直接导入做风险评估非常省事。所以SBOM不是“导出个依赖列表”这么简单它要解决的是三件事依赖关系完整、格式标准统一、可被自动化工具消费。想清楚这三点后面选工具、定流程才不会跑偏。2. 格式选型SPDX和CycloneDX别再选错SBOM的格式是第一个坑。目前主流的标准就是两个SPDX和CycloneDX。很多人一开始都会纠结选哪个我建议先花点时间搞清楚两者的侧重点不然换工具的时候会很痛苦。2.1 SPDX老牌标准许可证分析的好手SPDX由Linux基金会下的SPDX项目维护历史更久在许可证合规领域积累很深。它把组件、许可证、版权信息、交叉引用这些信息分门别类做许可证审计时非常好用。SPDX的典型输出是tag-value或JSON格式字段包括SPDXID、PackageName、PackageVersion、LicenseConcluded、CopyrightText这些。如果你所在的项目比较看重许可证合规比如法务部门要排查GPL传染性、Apache 2.0兼容性SPDX会更顺手。2.2 CycloneDX现代软件供应链更偏爱它CycloneDX由OWASP维护定位是“轻量级”但它现代生态支持得非常好。它除了能描述组件和版本还支持组件类型library、application、container等、漏洞披露vulnerability、服务依赖关系services这些信息。如果你要把SBOM传给漏洞扫描系统、对接风险评分平台CycloneDX是绝大多数工具的第一选择。我个人的习惯是默认选CycloneDX。原因很简单市面上主流工具Syft、Trivy、Dependency-Track、DefectDojo对CycloneDX的支持都很好尤其在自动化漏洞比对这个环节CycloneDX JSON格式可以直接被解析并关联漏洞库省了二次开发的工作。2.3 项目里怎么定格式这里给一个不太会出错的选型参考交付给外部客户、政企类项目JSON格式的CycloneDX附一份PDF或Excel汇总给人看。内部审计、合规审查SPDX CycloneDX双格式并行生成一个给法务一个给研发运维。容器镜像类基础设施CycloneDX JSON记录运行环境基础依赖已经足够。纯硬件嵌入式项目SPDX更适合因为要记录固件、BSP、内核以及许可证信息SPDX的传统字段更完整。注意格式一旦定了尽量保持稳定。因为下游的漏洞扫描、风险审计工具都是按字段解析的频繁换格式会导致部分工具重新导入后历史数据对不上。我在项目里吃过这个亏后面在“版本管理”部分会细说。3. 生成工具的选型逻辑与三条生成路线格式确定之后真正的重头戏是生成。工具选错、路线选错会直接影响SBOM的质量和可信度。3.1 三层生成路线我把SBOM生成方式分成三条路线构建解析路线读取源码项目的依赖清单文件package.json、requirements.txt、go.mod、pom.xml或锁文件package-lock.json、poetry.lock、Cargo.lock再解析出完整依赖树。构建工具集成路线通过Maven插件、Gradle插件、npm钩子、CI流水线脚本在构建过程中同步生成SBOM。它的优势是能拿到构建阶段才确定的信息比如profile启用的依赖、条件依赖。镜像/二进制扫描路线针对容器镜像、二进制产物做扫描分析通过指纹识别出里面包含的组件和版本。对大多数应用类项目路线1和路线2的组合就够了。如果你维护的是多语言微服务集群或者有很多遗留的老系统没法从源码构建路线3就是补救方案。3.2 我的工具选型清单这里是我在不同场景下实际用过的组合直接列出来供参考场景工具输出格式说明Node.js项目cyclonedx/cyclonedx-npmCycloneDX JSON读取package-lock.json速度快Python项目cyclonedx-pyCycloneDX JSON支持requirements.txt、poetry、pipenv、PDMJava/Maven项目cyclonedx-maven-pluginCycloneDX JSON构建插件可以打进package阶段Go项目syft packages dir:.CycloneDX JSON对Go module处理比较稳健通用源码工程syft packages dir:.CycloneDX JSON支持多种锁文件社区活跃容器镜像syft packages docker:xxxCycloneDX JSON扫描镜像里的系统包和语言包二进制/固件trivy rootfs、syftSPDX/JSON离线识别能力不错看到这里你可能会有疑问为什么不直接用一个工具通吃我的经验是单一工具很难在所有语言上都做到“精准”。例如cyclonedx/cyclonedx-npm对npm lock文件的支持非常精细但换到Python场景它就不工作了。syft虽然说支持多语言但它的原理是拿常见目录结构去匹配一旦你的项目结构比较特殊多模块仓库、私有registry它会有漏识别的情况。3.3 一个容易忽略的点生成SBOM不要只读清单文件很多人图省事直接根据项目根目录的package.json或requirements.txt做解析而不去解析锁文件。这是大坑。package.json只写了直接依赖的声明区间比如^1.2.0没有解析后的版本真正常驻的版本其实在package-lock.json、poetry.lock、yarn.lock里。如果直接基于声明区间生成生成结果里大量组件版本是模糊的后面做漏洞比对就完全没法用。所以生成SBOM的基础输入尽量选锁文件而不是声明文件。锁文件里记录了解析后的明确版本SBOM的可信度才高。4. 三个主流语言的实测生成过程光说不练假把式。这里把我在真实项目里跑的三个生成过程完整摊开包括命令、参数和验证方法照着操作基本能让你在自己的项目里快速跑通。4.1 Node.js项目生成示例先装工具npm install -g cyclonedx/cyclonedx-npm然后在项目根目录找到package-lock.json执行cyclonedx-npm --output-file bom.json --output-format json我建议重点看一下几个参数--output-file指定输出文件名默认是bom.xml。--output-format可选项是json或xml。--omit-dev如果生成的是生产交付版SBOM建议加上。开发依赖devDependencies里的测试框架、Lint工具不会出现在生产环境如果客户或安全平台看到了一大堆开发依赖会给你打回来说范围不清晰。我在一个中大型Node.js项目上跑过一次组件数量从直接依赖的120个膨胀到带传递依赖的400多个生成过程用了不到10秒。这个工具能自动识别项目里是否用了monorepo workspace多个子包会整合到同一份SBOM里省了很多手工合并的事。4.2 Python项目生成示例Python的依赖管理生态比较乱有pip requirements.txt、poetry、pipenv、PDM等多种方案。推荐用OWASP维护的cyclonedx-py它对这些生态兼容得比较全面。安装pip install cyclonedx-bom如果你还在用requirements.txtcyclonedx-py -e path/to/requirements.txt --output-file bom.json --output-format json如果你的项目用Poetrycyclonedx-py -e pyproject.toml --poetry --output-file bom.json --output-format json这里有一个实际经验当用到私有索引、本地路径依赖时SBOM里可能会出现pkg:pypi/private-libunknown版本号识别不到。这时候建议在生成前先执行一遍pip freeze再让工具基于pip freeze的输出做解析版本信息会完整得多。本质上tools需要从一份已解析的锁定环境出发去构建组件图。4.3 Java/Maven项目生成示例Maven项目我用的是官方插件plugin groupIdorg.cyclonedx/groupId artifactIdcyclonedx-maven-plugin/artifactId version2.8.2/version executions execution phasepackage/phase goals goalmakeAggregateBom/goal /goals /execution /executions /plugin直接执行mvn package随后在target目录里会多出bom.json和bom.xml两个文件。注意makeAggregateBom这个goal会把整个多模块项目聚合成一份SBOM比默认的makeBom更实用因为一个微服务往往由多个Maven模块组成如果只看单模块会丢一堆内部依赖。生成完之后建议检查一下SBOM里是否有Unknown版本的组件。Maven插件有时对系统依赖system scope或provided scope处理得不太理想这种情况可以手工在插件的excludeDependencies或includeDependencyGroup配置里管理范围。4.4 生成后必做的三件事看看总组件数一个合理的Java Spring Boot项目通常会有几十到上百个第三方组件如果生成完只有十几个组件那大概率是解析范围不对。抽样验证版本随手挑3~5个组件去本地~/.m2仓库或node_modules里核对实际版本号是否一致。跑schema校验CycloneDX发布方有官方JSON Schema建议把生成文件过一遍校验避免下游工具解析报错。npx cyclonedx/cyclonedx-cli validate --input-file bom.json --input-format json --output-format json5. 容器镜像与二进制场景从“代码物料”到“运行物料”上面讲的都是基于源码构建的生成方式。但现实里我们交付的东西未必是源码可能是容器镜像、一个二进制安装包甚至是嵌入式设备的固件。这时候还要使用另一套思路扫描运行物料本身。5.1 容器镜像扫描容器镜像里的内容不只是你代码的依赖还包括基础镜像如nginx、alpine、debian里的操作系统包。这部分信息靠读源码是拿不到的必须靠工具对镜像层做解析。我常用的流程是syft packages docker:nginx:1.27.4 --output cyclonedx-json --file nginx-sbom.json用Syft生成镜像的SBOM之后再配合Trivy或Dependency-Track做漏洞分析trivy image --input nginx.tar --format cyclonedx --output nginx-trivy.json一个常见误区是直接在容器里执行dpkg -l或者rpm -qa导出系统包列表就当成镜像SBOM。这有一定作用但系统包列表并不包含语言包依赖比如Python wheel、Node模块而且每个镜像层的哪些包最终生效也没法区分。用工具扫描虽然会慢一点但结果完整得多。5.2 二进制/离线场景如果是二进制安装包或固件推荐用Trivy的rootfs扫描能力trivy rootfs ./my-app-distro/ --format cyclonedx --output distro-sbom.json对于没有源码、没有构建环境的老系统这是最后一道保障。扫描结果里不一定能识别出所有组件部分商用组件、定制组件会落到Unknown类别但这不影响整体使用——你在交付时额外附一份手写清单把工具识别不了的补全就行。5.3 镜像基础SBOM和使用方式要分清楚现在很多软件项目会给自己发布的docker镜像附带一份SBOM比如Gitea、MinIO、Nginx。实际用这些镜像时建议直接下载官方镜像是SBOM做复核而不是重新扫描一遍。为什么因为镜像体积大扫描耗时且可能被防病毒软件拦截而官方构建时生成的SBOM是“原料账本”记录更准。不过这也要留个心眼SBOM不是“一次生成永久有效”的。镜像一重建依赖版本就可能变SBOM必须随之更新。所以接下来这部分我要重点讲流程化的问题。6. 把SBOM接入CI/CD和仓库版本管理单独跑一次命令生成SBOM只能算演示。真正要让SBOM发挥价值必须把生成过程固化到流水线和交付流程里。6.1 在CI流水线里自动生成以GitHub Actions为例一个Node.js项目可以这样加一个job- name: Generate SBOM run: npx cyclonedx/cyclonedx-npm --output-file sbom.json --output-format json --omit-dev - name: Upload SBOM to release uses: actions/upload-artifactv4 with: name: sbom-json path: sbom.jsonGitLab CI则可以在artifacts里带上sbom.json发布时一并归档。关键点是让SBOM生成与构建强绑定只要构建产物变了SBOM就必须同步更新否则出现“代码是新的物料清单是旧的”这种事故客户一比对版本号就会发现问题。6.2 在仓库里维护SBOM的几类做法目前业内常见做法有三类放源码仓库在项目根目录提交sbom.json。优点是天然跟随代码变更缺点是每次依赖变化都要生成并重建容易和CI自动化冲突产生大量无意义merge冲突。随发布包归档在release页面、容器registry、制品库Artifactory/Nexus里保存构建时生成的SBOM。这是最推荐的因为SBOM和具体发布版本强绑定。单独管理搭一个专用的SBOM管理平台比如OWASP Dependency-Track由流水线自动上报。适合安全团队集中管控很多应用的情况。我在团队里更推荐“制品库平台上报”双管齐下制品库保留原始文件Dependency-Track里再做后续的漏洞分析、风险告警。这样既满足交付又能做日常监控。6.3 CI里的SBOM质量门禁光生成不验证等于白搭。我建议在流水线里增加一个门禁步骤自动检查这几点组件数量是否低于某个阈值比如低于20个就要告警大概率是解析出错了是否存在版本为unknown的顶层组件顶层组件未知可能影响核对文件是否能通过官方schema校验是否包含预设的许可证字段缺失可选的合规项。这些检查可以用脚本写也可以直接用OWASP提供的cyclonedx-cli工具在CI里跑。6.4 我踩过的版本事故讲一个真实事故。当时有个微服务升级引用了新版本依赖CI自动构建生成了新的SBOM但制品库平台没做版本覆盖保留的是上一次的SBOM。客户那边拿到最新镜像导入SBOM到他们的安全平台一扫描发现漏洞列表和镜像不匹配。排查了一整天才发现问题不是扫描工具错了而是镜像和SBOM版本脱节。从那以后我就定了规矩SBOM文件名里必须带上构建号或镜像tag比如bom-app-api-v1.4.2-build-231118.json同时在制品库的metadata里记录SHA256。这样即使后面排查也能快速校对。你们在本地上传镜像或制品时一定要把文件名和使用上下文绑定清楚能省很多返工。7. 高版本不及格检查SBOM有没有“注水”和“缩水”生成SBOM看起来简单但质量参差不齐的情况非常常见。下面几种是我在审计中经常发现的问题以及对应的规避策略。7.1 组件“缩水”解析深度不够有些工具默认只扫描直接依赖没有递归展开传递依赖。结果输出的SBOM看起来干净清爽实际上漏洞分析阶段会漏掉大量被传递依赖带入的安全问题。Log4j那种漏洞之所以影响面大正是因为它嵌在很多组件内部只看到直接依赖根本发现不了。对策生成后用grep -c name粗略统计组件数量再和npm ls --all或mvn dependency:tree的总数对照偏差大就要检查工具配置。7.2 组件“注水”把开发依赖也算进生产环境我接手的很多项目里QA和Security部门拿到的是包含开发依赖的完整SBOM里面能看到Jest、Webpack、ESLint这些根本不会出现在生产容器的工具。这会导致漏洞扫描平台报出一堆“假阳性”处理告警的人力成本直接翻倍。对策生成部署SBOM时尽量排除dev依赖、test依赖、tooling依赖。但注意如果图省事在构建阶段强制排除所有dev依赖而项目里有依赖恰好只在生产环境使用且被标注为dev那会误杀。所以最好是在工具参数里明确指定范围而不是一刀切。7.3 版本字段错乱常见问题集中在这些地方版本号带了^或~前缀表明是声明范围而非锁定版本Git依赖的版本字段写成master分支名或commit hash的一部分外部包的pom.xml中version属性引用变量插件解析失败导致版本变unknown。对策在CI脚本里加一个小检查正则匹配version: [^0-9]或version: .*unknown.*有命中就输出warning。这种做法成本很低但能拦截大量明显的解析异常。7.4 重复组件与互相冲突的版本Java世界最常见的是jar包冲突。比如实际运行环境里某个类是从A.jar的1.2版本加载的但SBOM里同时列了1.1和1.2扫描平台会把两个版本的漏洞都抓出来导致误报。对策在生成SBOM时若工具支持“实际运行解析”能力优先用若没有至少在SBOM的scope字段标注required或optional方便下游处理。CycloneDX对scope有定义建议在生成时显式保留。7.5 许可证字段残缺不少公司做合规审查时重点关注许可证。默认生成的SBOM只有在工具能自动从包元数据里读取出license时才填入字段缺失率往往不低。对策如果许可证信息缺失可以使用Syft加--add-cpes-if-none或配置license扫描的插件在生成阶段就尝试从在线数据库补全。另外推荐用SPDX的LicenseRef做自定义补充清晰表达“工具识别不出人肉确认后的结论”。8. 我常用的落地模板从零到一搭一套SBOM管理机制最后分享一套我在实际项目里沉淀下来的落地模板你可以照着套用结合团队现状调整。8.1 模板SBOM管理清单序号环节要求1格式标准统一CycloneDX 1.5/1.6 JSONSPDX做备份2生成时机每个正式构建/发布时自动生成3生成范围运行时依赖依场景排除开发依赖包含传递依赖4版本绑定文件名携带构建号/镜像tag记录SHA2565验证动作schema校验、组件数量阈值校验、版本异常校验6归档位置制品库安全平台上报7更新机制依赖变化后强制生成新SBOM禁止手工修改旧文件8消费场景交付客户、漏洞扫描、合规审查、上线审批8.2 模板分阶段落地路径第一阶段1~2周在主要服务的CI里接入工具自动生成SBOM并在制品库归档。先不用做门禁和平台对接目标是有。第二阶段1个月接入schema校验和数量阈值校验顺手解决生成报错和版本异常问题。目标是准。第三阶段1~2个月对接Dependency-Track或企业已有SCA平台建立漏洞监控和工单分发。目标是用。第四阶段把SBOM纳入发布检查单和上线审批联动让每个发布版本都必须附带合规的SBOM。目标是管。老实说很多团队一上来就想直接做到第四阶段结果连第一阶段的工具选型都没走通。SBOM是个系统工程别贪心先从最简单的自动生成开始跑通后再逐步加质量控制和消费场景稳扎稳打比一步到位实际得多。9. 一些小而重要的细节补遗这部分是一些分布式效果不起眼但经常卡住人的零碎经验整理在这里供参考。私有registry、私有npm包、私有Maven仓库在生成时经常报错。处理办法是在流水线里提前暴露认证凭证给工具比如npm工具的--registry参数或Maven的settings.xml。离线环境生成SBOM建议用syft或trivy的本地缓存和--offline模式不要在生产网段里依赖线上数据库。SBOM的使用频率比很多人想象得高。每次做上线评审时把SBOM和依赖变更记录一并提交审计方只需要“扫一下”就能过大大减少来回解释的成本。大型系统的SBOM可以拆分成多个子SBOM。一个服务一个SBOM容器一个基础SBOM最后再做一个聚合SBOM。过度聚合大而全的SBOM会导致下游工具解析变慢。关于那个声称“SBOM sytf下载”的话题我补充一句现在不少开源项目官网或Docker Hub页面会提供SBOM文件下载但下载后一定要做两步一看版本号是否和实际制品一致二看文件hash是否和制品的构建记录对得上。就算是官方提供的SBOM校验这两个信息也花不了几分钟但能让交付质量提升一大截。如果你下载后是要导入自己的风险平台做对比那就更要先过一遍schema校验免得折腾半天导了个不可用文件。到目前为止我用了大量时间推动团队把SBOM当成“软件交付的基本凭证”来做。效果很明显每年各种平台升级时以前可能要加班一天排查哪些组件要重新发布现在直接拿SBOM和漏洞库比对几十分钟就能输出结论。这种“让依赖透明”的事越早做后面的安全感越足。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →