简介Fortify SCA 20.1.1 是一套面向开发团队和安全人员的源码审计工具可在编码阶段定位注入、跨站脚本、缓冲区溢出等典型漏洞支持二十六种编程语言覆盖网站、移动应用与后台服务等多种项目类型。资源共四十一个文件以 bin 规则库、jar 组件、exe 安装程序及授权文件为主bin 文件承载各语言的分析规则jar 为运行核心exe 用于 Windows 部署整体约九百八十七兆字节。工具内置一千零一十九个安全漏洞类别支持自定义审计规则可与 Git 等版本库和 Jenkins 等自动化部署工具联动同时提供主流集成开发环境插件便于开发者在编码过程中实时接收安全反馈。现有九百二十人学习下载包内附带授权文件与说明文档可辅助完成授权配置和规则加载便于直接开展代码审计实践。1. 代码审计工具 Fortify SCA 20.1.1为什么静态分析仍是安全左移的硬门槛Fortify SCA 20.1.1 是代码审计工具里被点名率最高的一款下载地址好找但真正把它跑起来并让报告可信的人不多。常见的原因不是扫描器不行而是环境没配对、规则包没更新、命令行参数用错。静态分析作为安全左移的第一道闸门靠人工 code review 根本追不上版本迭代只有把 SAST 嵌进提交前检查才能在漏洞进主干前拦截。这篇笔记适合两类人一类是刚接手代码安全评审的开发者想知道这个工具到底怎么扫另一类是已经用过但被误报和配置折腾过的工程师建议直接从避坑那章看起。2. 从扫描规则到数据流引擎Fortify SCA 的分析链路与规则包选型2.1 扫描器、规则库、数据流引擎三层各管什么在配置之前先弄明白 Fortify SCA 的扫描链路是怎么组织的。它不是一个简单的“丢代码进去吐漏洞”的黑匣子而是由三个协作层构成前端是扫描器负责解析源代码、识别语言版本和构建方式中间是规则库里面定义了每种漏洞的模式比如 SQL 注入的污点源、污染传播路径和不安全函数调用底层是数据流引擎负责跨函数、跨文件追踪这些污点判断它们是否真正到达了风险点。这三层里最容易出问题的是规则库。默认规则包覆盖面很广但不同团队的技术栈差异很大比如只用了 MyBatis 的持久层和用 JPA 的持久层SQL 注入的触发模式完全不同。所以 20.1.1 提供了规则库管理工具可以按语言、框架、CWE 编号启用或停用。常见做法是先跑一个默认扫描再根据报告里的漏洞分布调整规则包避免大量规则在无关代码上浪费扫描时间。数据流引擎也需要理解一个关键点它设计为“宁可多报不可漏报”。这意味着跨方法调用的传递会尽量展开直到遇到无法解析的反射或动态代理才停止。所以扫描慢不一定是你项目大也可能是动态特性过多导致引擎在推导调用关系时耗尽资源。理解这一点后面排查性能问题就有了方向。2.2 规则包的更新与自定义规则怎么写20.1.1 的规则包更新是一个独立命令叫 fortifyupdate。它从官方规则仓库拉取最新的扫码规则覆盖新披露的 CWE 类型和常用框架的污点模式。更新前建议备份当前规则目录因为某些版本会出现新旧规则并存导致的重复报漏洞。备份后进入安装目录下的 bin 文件夹运行命令cd /opt/fortify/SCA/bin ./fortifyupdate -acceptKey这里的-acceptKey是接受远程仓库的安全密钥首次运行时必须带否则会停在交互确认。更新完成后会提示规则包版本号我一般会记下来因为后续扫描报告里会用到这个版本信息。注意 fortifyupdate 没有增量更新选项每次都是整体同步所以网络差的时候容易超时建议设置较长的连接超时或者选择在低峰时段跑。如果内网环境无法访问官方仓库另一个思路是手动导入规则包。把公司安全团队导出的 Rulepack 文件放到 rules 目录下然后用fortifyupdate -include指定目录。这种方式适用于内部定义的漏洞模式比如自研框架里的敏感数据脱敏检查。自定义规则通常需要在 Audit Workbench 里装规则编辑器插件写 XML 描述污点源和数据流路径。入门时不用从零写可以从现有规则里复制一个类似的改 CWE 编号和匹配模式。需要注意的是自定义规则文件名不能有中文且 XML 必须带 schema 版本声明否则会被静默忽略。还有一个实际经验规则包更新后旧的扫描结果用新规则重新打开往往会新增一批“潜在漏洞”。这是正常现象但会导致同一个 build id 的增量扫描差异变大。所以每次升级规则包我建议先把历史 FPR 归档再重新建立基线而不是直接拿新规则去比对旧结果。2.3 命令行扫描的完整流程从 clean 到 -scan拿到一份源码后我习惯先把扫描命令拆成两步translate 和 scan。translate 是把源代码解析成中间模型生成一个 .fprFortify Project Results文件的中间状态scan 是在中间模型上运行数据流分析。实际执行的命令通常合并在一起但理解这两个阶段对排错很有帮助。sourceanalyzer -b demo_project -clean -Xmx4G -source 1.8 \ -cp lib/*:target/classes src/ sourceanalyzer -b demo_project -scan -f demo.fpr \ -format fpr -build-project demo_project第一行先创建 build id 为 demo_project 的会话-clean清掉上次的临时文件避免增量数据残留。-Xmx4G是给翻译阶段预留的堆内存源码量大的时候 2G 经常不够-source 1.8声明源代码的 Java 版本不写的话工具会根据字节码猜测可能会猜错。类路径-cp非常关键如果第三方库没加到类路径里数据流引擎会把很多调用判成“不可达”导致漏报。第二行执行扫描-f指定结果文件路径-format fpr是默认格式但显式写出来更清晰。-build-project是告诉工具复用刚才那个 build id 的翻译结果。扫描完成后会得到 demo.fpr这不是给人读的而是给 Audit Workbench 或报告生成器用的。要直接看 PDF可以加-format pdf并指定-f demo.pdf但 PDF 适合存档不适合研判。研判阶段最好还是用 .fpr 打开审计工作台。关于 build id还有一个容易忽略的点翻译阶段的-b名称如果和扫描阶段不一致工具会认为没有可用模型直接报“Project not found”。我见过不少同事在这个小坑上耗了半天。所以建议用同一个 build id并且用-clean时也要带这个 id否则清理的可能是另一个会话。另外如果项目是 Maven 或 Gradle 构建推荐在翻译前先跑一次构建生成最新 class 文件Fortify SCA 也能通过-maven或-gradle参数自动接管但自动接管的翻译准确率不如手动指定类路径所以我一般只用它做初步摸底。翻译阶段常见的报错有两种。一种是在解析依赖时提示“cannot find symbol”这说明类路径没写全需要把依赖 jar 的路径补进-cp。另一个是内存溢出通常发生在-source版本设置不正确时编译器尝试加载大量符号可以使用-Xmx提高堆内存。注意这里的-Xmx是给 Fortify 自身 JVM 用的不是给源码编译用的两者别混淆。我通常先在本地用 4G 试跑如果翻译阶段超过 20 分钟会考虑拆分扫描模块而不是盲目加内存。3. 安装与许可证配置JDK 版本、授权文件与扫描环境搭建3.1 环境依赖清单与版本兼容20.1.1 这个版本的安装包约 1GB 左右解压后主要包含 bin、lib、rules、plugins 等目录。它不要求必须装在某固定位置但路径最好不要带空格和中文否则后续命令行参数解析会出错。官方支持的操作系统范围很广Linux、Windows、macOS 都有对应安装包但部署在 Linux 服务器上是最常见的做法因为后续要接 CI 流水线。先列一份环境依赖清单组件版本要求说明JDK8 或 1120.1.x 对更高版本支持不稳定内存至少 4G扫描大项目建议 8G 以上磁盘留 10G规则库和中间文件占用可观操作系统主流 Linux 发行版glibc 版本太低会起不来JDK 版本是第一个坑。我见过有人用 JDK 17 启动 Fortify结果扫描器直接抛 UnsupportedClassVersionError。Fortify SCA 20.1.1 的启动脚本会按 JAVA_HOME 去找 Java而且它内部运行的是 64 位 JVM所以 32 位 JDK 也起不来。建议用 OpenJDK 8 或 OpenJDK 11这两个组合我都实测过可以跑通。装好后用java -version确认并确保 JAVA_HOME 指向的是 JDK 根目录而不是 JRE 目录。3.2 静默安装与手动安装步骤Linux 下的安装包很多是 zip 或 tar.gz解压即用但也有 bin 安装脚本的形式。这里说一个比较稳妥的手动安装流程mkdir -p /opt/fortify tar -zxvf fortify_sca_20.1.1.tar.gz -C /opt/fortify cd /opt/fortify/SCA/bin chmod x sourceanalyzer fortifyupdate echo export PATH$PATH:/opt/fortify/SCA/bin ~/.bashrc source ~/.bashrc解压后不要急着跑扫描先看一眼 bin 下有没有sourceanalyzer这个文件它是所有命令行操作的主入口。有些发行版的 unzip 对长路径名处理得不好解压出来文件缺失所以推荐用 tar。如果拿到的是安装脚本需要用./install.sh -s静默安装-s表示跳过交互向导参数可以在 response 文件里写好。response 文件是文本指定安装目录和是否安装远程代理我一般只装本机扫描组件远程代理不装省资源。安装完成后用sourceanalyzer -version验证。如果提示找不到命令多半是 PATH 没生效或者是执行权限没加。注意 sourceanalyzer 是 shell 脚本封装的里面会调用 lib 下的 jar不要试图直接用 java 启动。如果 shell 脚本报错先看它调用的 java 版本可以在脚本头部临时加一行echo $JAVA_HOME调试。Windows 下的安装大同小异但要注意两个坑一是安装路径不能放在系统权限受限的目录下因为权限控制可能导致规则缓存写入失败二是运行 sourceanalyzer 需要以管理员身份开启命令行否则访问某些系统目录会报访问拒绝。我通常把 Fortify 装在自定义工具目录然后手动把 bin 加入系统 PATH。如果不想改环境变量也可以用命令窗口里的 set PATH 临时指定但后续接流水线时会变得很麻烦还不如一步到位。3.3 许可证文件放置与规则库加载验证Fortify SCA 是商业授权工具安装完成后需要把许可证文件放到指定目录。许可证文件里包含了授权期限、规则包版本和允许的扫描并发数。常见做法是把许可证放在安装目录下的一个固定文件里或者通过环境变量指定。我一般是把许可证放到/opt/fortify/fortify.license然后在启动脚本里设置export FORTIFY_LICENSE_FILE/opt/fortify/fortify.license export FORTIFY_RULES_DIR/opt/fortify/SDA/rulesFORTIFY_LICENSE_FILE是许可证文件路径FORTIFY_RULES_DIR是规则包目录。两个变量不设置的话工具会去默认路径找找不到就会在日志里报 license not found。注意这两个变量名在不同版本里可能有差异20.1.1 里常用的是FORTIFY_LICENSE_FILE也有老版本用其他变量。设置完用 fortifyupdate 拉一次规则包然后执行一个最小扫描验证。echo public class Test { String s null; } /tmp/Test.java sourceanalyzer -b verify -clean -source 1.8 /tmp/Test.java sourceanalyzer -b verify -scan -f /tmp/out.fpr -format fpr sourceanalyzer -b verify -scan -f /tmp/out.html -format html如果 license 有问题第二步会直接报许可证错误如果规则库没加载结果文件会生成但内容为空。所以这一步是给整个环境做冒烟测试。另外许可证有过期时间20.1.1 的试用版一般只有 90 天过期后扫描会提示需要续期。这个不要去找奇怪的补丁直接申请正式授权即可否则规则包更新也会失败。许可证还有一个隐藏的限制它绑定了目标平台数量和规则包版本。换主机或者升级规则包都可能触发重新校验。遇到这种情况需要联系授权方重新生成许可证文件。在正式环境里我至少保留两份许可证备份一份放在构建服务器一份放在本地避免因为单点故障导致整个安全门禁停摆。4. 命令行实战用 Fortify SCA 扫描模拟项目并输出审计报告4.1 准备被测项目与扫描目标为了演示完整流程我用一个典型的 Java Web 模拟项目X来做例子。这个项目结构如下一个控制层 Controller一个业务层 Service一个 DAO 层数据库访问用的是预编译 SQL 和字符串拼接混用。这种项目最能体现 Fortify SCA 的价值——字符串拼接的 SQL 会触发 SQL 注入漏洞预编译的部分则保持干净正好用来验证工具是否区分两类写法。先把项目放到干净的目录确保代码能独立编译。Fortify SCA 翻译阶段不需要你执行编译但它需要能解析到依赖。模拟项目X的依赖集中在 lib 目录同时把编译好的 class 文件放到 target/classes 下。用 Maven 的话可以先执行一次mvn clean package确保依赖齐全。然后写一个扫描脚本把构建 id、源码路径、类路径都放到变量里方便复用。#!/bin/bash BUILD_IDx_demo SOURCE_DIR/data/projects/x_demo/src CLASSPATH/data/projects/x_demo/lib/*:/data/projects/x_demo/target/classes JDK_LEVEL1.8 OUTPUT_DIR/data/reports mkdir -p $OUTPUT_DIR sourceanalyzer -b $BUILD_ID -clean -source $JDK_LEVEL -cp $CLASSPATH $SOURCE_DIR这里把-clean和翻译放在同一行意思是每次从头构建模型。实际项目中如果只改了几个文件可以去掉-clean用增量模式加快速度。但首次扫描或依赖变化后强制 clean 一次更稳妥。类路径里使用了通配符lib/*Fortify 自己会展开省得手写每个 jar。注意-source要放在-cp之前或之后都行但必须放在源码路径之前否则会把源码路径误认成参数。4.2 执行扫描并生成 PDF/HTML 报告翻译完成后下一步是扫描和生成报告。这里我会同时生成 FPR 和 HTML 两个产物FPR 用于后续审计HTML 用于快速分享给开发同事。命令如下sourceanalyzer -b $BUILD_ID -scan -f $OUTPUT_DIR/x_demo.fpr -format fpr sourceanalyzer -b $BUILD_ID -scan -f $OUTPUT_DIR/x_demo.html -format html -so第二个命令里的-so是 scan only 的缩写表示只做扫描分析不重新翻译直接复用第一个命令的中间模型。如果不写-so它会重新走一遍翻译白白浪费几分钟。有的版本还用-f来区分产物但-format必须对应文件后缀。有人把 HTML 报告当成固定网站在浏览器里看这是可以的但 HTML 报告默认只包含摘要和漏洞分类不包含具体代码位置的上下文所以用来做周报可以用来修漏洞还是得回到 FPR。扫描时间取决于项目规模和规则包数量。模拟项目X只花了 3 分钟就完成了这是一个适度规模的效果。如果发现单次扫描超过 30 分钟不要直接 kill 进程先看是不是规则包加载了过多冗余数据流规则。Fortify 20.1.1 支持通过-scan-policy参数指定扫描策略我一般用默认策略但会先把不用的语言插件从 rules 目录里移走比如纯 Java 项目不需要其他语言的规则这能明显降低内存占用和扫描时间。PDF 报告生成通常需要额外的模板配置。Fortify SCA 自带了一个默认的 PDF 模板直接运行-format pdf就能出但默认模板是英文的。想要中文报告需要修改报告定义目录下的配置文件把 locale 改成中文。这个操作有点繁琐还涉及字体文件如果只是给国内团队看我建议直接生成 HTML 后用浏览器翻译或者导出 Excel 给开发自测。报告的形式是次要的核心是漏洞位置和修复建议要准确。4.3 用 FPR 和审计视图做误报研判拿到 .fpr 后用 Audit Workbench 打开。AWB 的界面会展开左侧的漏洞列表右侧是详细的调用路径和代码上下文。第一次打开可能提示“结果文件版本高于当前工具版本”这多半是因为做过规则更新导致 FPR 格式版本不一致。解决办法是升级 AWB 版本或者重新用同一个版本的 fortifyupdate 更新规则后再次扫描。在 AWB 里我习惯先按 CWE 分类排列高优先级的看危险函数调用链。模拟项目X扫出来的 SQL 注入会显示一条从 Controller 接收请求参数经过 Service 层字符串拼接最后到达 DAO 层的 Statement.execute。这条链路是可信的因为每一步都能在源码里看到实际调用。但也有误报比如某个 sanitize 方法实际做了转义但规则库里没有对应的清洗函数定义工具就会把后续的 sink 判定为污染。遇到这种情况不要直接忽略而是右键标记为“Not an Issue”并添加到项目审计差异库中。审计差异库可以理解为一个针对该项目的误报白名单。同样的问题下次扫描时会被自动跳过。不过不要滥用——标记之前必须确认这里确实做了足够的过滤或者输入不可控。我见过有同事为了通过门禁把几十条真实漏洞标成误报结果上线后被攻击者打穿了。研判是审计师的责任不能交给工具。导出 Excel 可以通过 AWB 的“导出报告”功能选择 xlsx 格式。每条漏洞会带上文件路径、行号、CWE、严重级别和建议。开发拿到这个表格后可以直接按行号定位代码这也是团队每周安全日用的格式。注意导出的行号是基于翻译时的源码版本如果代码在扫描后又被改动过行号会有偏移所以最好把扫描时的 git commit 记录也一并发出去。5. 避坑Fortify SCA 使用中的五个常见翻车点与排查记录5.1 环境与安装类翻车点这一章的坑都是一行日志一行日志抠出来的。静态分析工具不像普通软件出了问题不会弹窗告诉你只会往后台日志里写一条不容易看懂的消息。所以排查时要带着“先环境后参数”的思路。翻车点 1sourceanalyzer 启动报 UnsupportedClassVersionError现象运行sourceanalyzer -version时终端抛出一长串 Java 版本错误工具无法使用。原因JAVA_HOME 指向的 JDK 版本太高20.1.1 的脚本编译产物用的是 Java 8/11 字节码JDK 17 以上的 JVM 虽然能向下运行但 Fortify 内部的部分辅助库没有兼容。此外32 位 JDK 也会导致加载不到正确的本地库。解决统一安装 OpenJDK 8推荐或 OpenJDK 11并确认 JAVA_HOME 指向的是 JDK 目录而不是 JRE。如果机器上装了多个 JDK可以临时改环境变量export JAVA_HOME/opt/jdk8再试。注意在 Linux 上用切换工具改了版本后要重新登录否则环境变量不刷新。翻车点 2许可证文件读取失败扫描被中止现象翻译代码正常但执行-scan时提示许可证已过期或文件无效。原因许可证文件路径设置错误或者文件权限不够。Fortify 启动进程的用户可能不是文件 owner读不到自定义目录下的 license。解决将 license 文件放到固定目录并赋予 644 权限然后在启动命令前显式 exportFORTIFY_LICENSE_FILE。如果用的是试用许可注意过期日期到期前联系授权方续期。不要试图通过改系统时间绕过那样会导致规则包时间戳校验失败产生更难排查的问题。翻车点 3项目路径含中文或空格导致翻译失败现象翻译阶段报告Unable to create model for file或者生成结果文件为空。原因Fortify 内部的临时目录和文件名拼接对特殊字符很敏感。Windows 下路径中的中文在 zip 解析阶段可能被转成乱码空格则会导致 shell 把参数拆开。解决把整个项目复制到纯英文路径例如/data/ci/projects/app_x并保证目录名没有空格。这个限制虽然很不现代但在一线实操里就是存在我一般会在项目初始就约定命名规范。5.2 扫描与结果类翻车点翻车点 4扫描过程中内存溢出OOM 后没有产出现象扫描进行到一半日志出现java.lang.OutOfMemoryError: Java heap space进程退出没有 .fpr 文件。原因翻译阶段用-Xmx4G但扫描阶段复用同一个堆设置面对大型项目或过多规则包时内存不够。Fortify 默认会在临时目录保存中间表示文件磁盘不足也会间接导致内存交换。解决给扫描阶段单独指定更大的内存例如-Xms2G -Xmx8G同时清理临时目录/tmp/fortify 或用户目录下 .fortify。如果项目过大考虑拆成多个模块分别扫描最后用 Fortify 的合并工具合并结果。合并命令是fortifymerge -f merged.fpr a.fpr b.fpr但注意合并后行号会按照各自模块相对路径保留不会错乱。翻车点 5扫描完成但漏洞列表里大量 unresolved 方法现象报告中许多漏洞的调用链断在半路显示unknown method或unresolved target。原因数据流引擎无法解析依赖类类路径没配全是最常见原因。另一个原因是代码里大量使用了反射、动态代理或 ServiceLoader 这种运行期绑定机制。解决先检查-cp是否覆盖所有 jar。对于反射和代理只能依靠自定义规则去描述入口点或者把这些调用配置为规则配置里的“纯净方法”。如果不想过度投入可以先接受误报但要把这些 unresolved 报告单独导出交给研发评估。重点是不要因为 unresolved 多就认为工具没用它恰恰暴露了项目的动态调用黑盒。以上五个翻车点是我在实际使用中遇到频率最高的。每个问题都不难解决但都容易在时间紧迫时让人抓狂。如果你也遇到类似情况建议按这个顺序排查先看环境变量再看文件路径最后才怀疑工具本身。6. 把审计结果接进 DevSecOps增量扫描与 CI/CD 集成的加速技巧6.1 增量扫描只扫变更代码省一半时间Fortify SCA 支持基于文件时间戳的增量翻译。第一次全量扫描后后续在相同 build id 下运行不带-clean的翻译命令工具会自动对比时间戳只解析改变的源文件。命令就是把-clean去掉其余参数保持一致。这个机制依赖临时目录里的模型缓存所以构建 id 不能乱改。我一般会在每次代码合并请求时先走一遍全量基线然后与上一次基线做 diff只扫描 diff 出来的文件路径。注意如果改动涉及接口签名或公共类依赖它的文件也需要一起扫工具可能做不到全自动传递我会在 diff 列表里额外加上编译时依赖分析产出的文件清单。6.2 与 CI/CD 流水线对接的常见姿势把 Fortify SCA 放进流水线建议用独立的 agent 节点避免和其他构建任务抢占内存。流水线里核心的步骤是拉代码、构建、翻译、扫描、归档结果、判定门禁。下面是一个精简的流水线片段stage(Fortify Scan) { sh sourceanalyzer -b ${app} -clean -source 1.8 -cp ${classpath} src/ sourceanalyzer -b ${app} -scan -f ${app}.fpr -format fpr archiveArtifacts artifacts: *.fpr, *.html }这里要注意一个问题流水线里每次执行都是全新工作区临时模型缓存不会保留所以每次都等同于全量扫描。如果不想这样需要配置一个持久化的工作目录。一般团队会忽略这一点导致“增量扫描”在 CI 里根本没生效。另一个细节是类路径里的 jar 版本要和构建阶段一致最好直接用构建产物的 lib 目录而不是让 Fortify 自己去依赖仓库拉。6.3 自定义规则包收敛误报的关键动作接了流水线之后最大的痛点是误报导致门禁频繁失败。我的习惯是把 AWB 里确认过的误报记录导出为审计差异文件这个文件可以放到项目仓库里每次扫描前自动应用。自定义规则也可以做成统一的内部规则包放置到规则目录下这样所有分支共用一套标准。关键动作是每周审视一次误报清单把重复出现的模式提炼成规则或过滤器而不是每次都人工点掉。最后说一个我在一次升级时的教训当时我为了赶版本在没验证新规则包的情况下就全局替换了规则目录结果规则版本不匹配导致历史 FPR 全部打不开。从那以后我每次在 CI 里改规则或升级工具都会先在一个单独的分支上做全量扫描对比确认新增漏洞和误报比例都在可接受范围再切到主干。希望帮到你。本文还有配套的精品资源点击获取