SCA 工具在识别和管理应用程序中的第三方依赖及风险方面起着至关重要的作用,是云原生时代下数字供应链风险治理必不可少的基础设施。然而,基于源代码和二进制制品的静态 SCA 工具在落地实践时通常会面临两个让安全运营人员和研发人员头疼的问题:误报和修复优先级。
SCA 工具为用户(主要是开发人员和安全运营人员)提供了两大核心能力,一是对软件组成成分进行梳理,形成软件物料清单(SBOM);二是基于 SBOM 内容,对其中涉及安全漏洞或许可合规风险的组件进行告警。
所谓“警报疲劳”是指用户被 SCA 工具的海量报告淹没,导致疲于对漏洞修复优先级排序或对误报进行处理。造成这一问题的原因首先是由于现代软件变得越来越复杂,一个应用程序通常会有数十或数百个直接依赖,其间接依赖数量更是直接依赖的两倍甚至更多;其次,SCA 工具难免会产生一些误报:报告存在已知风险但并未被真正引用或无法直接利用的组件。这种误报会导致开发人员对工具失去信心,即产生“警报疲劳”。
SCA 工具提供的上下文信息不足也是导致“警报疲劳”的原因之一。如果 SCA 报告中没有提供已识别风险的影响、严重程度、修复建议等信息,研发人员就需要花费更多的精力在了解漏洞细节上,增加了修复漏洞的平均时间。 为了减轻“警报疲劳”,需要提高漏洞的检测的准确性、减少误报、对海量结果修复的优先级进行排序,提供更详细的漏洞描述信息。然而,实现上述功能对静态 SCA 工具来说,仍有较长的距离。
为了避免“警报疲劳”,一个行之有效的方案是对漏洞的可达性进行分析。
可达性是一个用来确定应用程序中的任何调用路径是否可以到达某个脆弱函数的术语。这种分析有助于理解一个存在于间接依赖中的漏洞如何被到达和利用。直接依赖是与代码直接关联的,开发者通常清楚其作用;间接依赖是直接依赖本身所依赖的组件(一般在构建过程中间接导入所需的代码),可能开发者并未意识到。可达性分析不仅是评估软件漏洞风险的关键概念,也是轻松修复漏洞的关键。通过分析软件架构中的各种路径和依赖关系,可达性有助于识别潜在的攻击向量,以及修复的最快方式。理解可达性有助于优先考虑修复工作,引导开发者关注直接依赖可以提高修复的效率和速度。它还有助于通过确定哪些第三方包负责大部分漏洞并需要打补丁,来识别对组织的真正风险。
可达性分析主要有静态分析和运行时分析两种方式:
运行时SCA在数字应用的测试和上线运营阶段进行组件级资产测绘,实现准确评估数字业务在运行时真实加载的第三方组件及相关风险。运行时SCA可通过运行时监控技术,检查程序运行时加载的第三方组件,可排除未执行加载的冗余组件,相较于静态SCA,运行时SCA检测精度更高。
如《IAST 技术进阶系列(一):关键语言支持》中所介绍,运行时 SCA 需要根据不同编程语言特性单独编写“探针”,来获取加载到应用运行时环境的组件信息。通常来讲,按照是否需要编译可将编程语言分为三类:编译型、解释型和混合型。
通过上述方式获取到运行时 SCA 的依赖信息后,就可以像静态 SCA 工具一样,通过知识库匹配得到更详细的组件信息和漏洞信息了。
图1:运行时SCA技术基本原理
静态 SCA 工具通常会依赖包管理器配置文件或二进制特征指纹识别组件,若组件被声明在依赖中,但代码中并未引用,就会存在误报。而通过运行时 SCA,便可以准确拿到在应用运行时阶段使用了哪些第三方组件依赖。如下图所示,为检索所有在运行时环境中依赖 Log4j2.x 组件的所有应用。
以 Java 为例,为了实现漏洞可达性分析,我们需要先通过 JVM Instrumentation 接口的方法获取加载到运行时环境的所有类。结果部分内容示意:
... java.util.concurrent.Executors java.util.concurrent.Executors$DefaultThreadFactory org.apache.logging.log4j.core.LifeCycle$State org.apache.logging.log4j.internal.LogManagerStatus org.apache.logging.log4j.core.LoggerContext org.apache.logging.log4j.spi.Terminable org.apache.logging.log4j.core.config.ConfigurationListener org.apache.logging.log4j.spi.LoggerContextShutdownEnabled org.apache.logging.log4j.core.AbstractLifeCycle org.apache.logging.log4j.core.util.ExecutorServices org.apache.logging.log4j.core.config.NullConfiguration org.apache.logging.log4j.core.config.AbstractConfiguration org.apache.logging.log4j.core.filter.AbstractFilterable org.apache.logging.log4j.core.Filter org.apache.logging.log4j.core.async.AsyncLoggerConfigDelegate org.apache.logging.log4j.core.util.Watcher org.apache.logging.log4j.core.lookup.StrLookup org.apache.logging.log4j.core.Layout org.apache.logging.log4j.core.layout.Encoder org.apache.logging.log4j.core.Appender org.apache.logging.log4j.core.net.Advertiser org.apache.logging.log4j.core.util.NanoClock org.apache.logging.log4j.core.config.ConfigurationSource org.apache.logging.log4j.core.config.Property org.apache.logging.log4j.core.config.DefaultAdvertiser org.apache.logging.log4j.core.lookup.Interpolator org.apache.logging.log4j.core.lookup.AbstractConfigurationAwareLookup org.apache.logging.log4j.core.config.ConfigurationAware org.apache.logging.log4j.core.lookup.AbstractLookup org.apache.logging.log4j.core.lookup.MapLookup org.apache.logging.log4j.core.lookup.Log4jLookup org.apache.logging.log4j.core.lookup.SystemPropertiesLookup org.apache.logging.log4j.core.lookup.EnvironmentLookup org.apache.logging.log4j.core.lookup.MainMapLookup org.apache.logging.log4j.core.lookup.MarkerLookup org.apache.logging.log4j.core.lookup.JavaLookup org.apache.logging.log4j.core.lookup.LowerLookup org.apache.logging.log4j.core.lookup.UpperLookup org.apache.logging.log4j.core.lookup.JndiLookup org.apache.logging.log4j.core.lookup.JmxRuntimeInputArgumentsLookup org.apache.logging.log4j.core.lookup.DateLookup org.apache.logging.log4j.core.lookup.ContextMapLookup ...
通过上述信息,即可对存在漏洞的类进行检索。例如 Log4j2 漏洞,若 CVE 没有披露 Log4j2 中具体哪些类存在风险,那么在上述列表中只要涉及一条 Log4j2 的记录,配合组件版本识别,就足以证明该应用存在风险,可以作为修复漏洞的理由;若 CVE 中提供了这些信息,那么开发人员就可以利用这些信息进一步确认漏洞在自己的应用中是否可达,或者安全运营人员通过对风险函数进行埋点,通过 PoC 验证漏洞是否可达。
此外,也可以反方向进行排查。例如,先圈定需验证可达性的漏洞范围,对这些漏洞涉及的类及方法进行埋点,然后通过 PoC 或者 DAST/Fuzzing 工具等方式进行测试,验证漏洞是否可达。这个方案适合周期性地对一批应用进行筛查。例如若要在一批应用中排查是否涉及 Fastjson远程代码执行漏洞(CNVD-2019-22238),可在com.alibaba.fastjson.parser.DefaultJSONParser$parseObject 处埋点,通过构造利用 Payload 验证其是否可达。在灵脉 IAST 中验证情况如下:
图2:风险方法埋点
图3:可达性验证结果
运行时 SCA 不仅可提供运行时依赖的监控和审查,也可以通过植入额外的代码来实现运行时威胁的自我免疫(RASP)。实现 运行时威胁免疫有以下两个思路:
以Java为例,当由用户代码、第三方组件等代码一起编译生成的混源应用制品与RASP探针共同加载到JVM虚拟机中时,在Instrumentation API的transform阶段,我们不仅可以动态地修改目标Hook方法,同时也可以获取到所有加载到JVM的第三方组件信息。此时,借助云端运行时SCA分析引擎,即可获得在运行阶段加载第三方组件的风险情况,借助热修复补丁库,云端可自动下发对应的热修复补丁,动态地将风险组件的风险方法屏蔽或注入额外的安全判断逻辑,在不直接修改源代码情况下实现对风险组件的代码级加固。
关于运行时威胁免疫的更多原理介绍,也可参考RASP技术进阶系列(四):基于安全共生的供应链安全风险防御。
运行时 SCA 作为静态 SCA 工具的补充,可以覆盖软件生命周期的更多阶段,对运行时应用实现更加便捷且自动化的依赖风险发现、验证、处置的闭环管理流程,解决开源组件漏洞治理难以落地的难点。
如下图所示,将源码 SCA 工具与运行时 SCA 相结合,覆盖应用从编码、测试、上线运行多个阶段,配合供应链安全情报预警,实现漏洞利用可达性验证闭环。
图4:漏洞利用可达性验证
收敛流程如下图所示:
图5:漏洞风险收敛流程
通过该方案,将静态 SCA、供应链安全情报、运行时 SCA 能力集成到软件生命周期中,通过漏洞严重性、漏洞可利用性、漏洞可达性、修复稳定性几个维度,确定漏洞修复的优先级,将安全运营人员和开发人员从“警报疲劳”的苦海中解脱出来,将精力集中到关键漏洞上。
伴随着应用执行,运行时SCA可结合代码疫苗热补丁技术对漏洞进行动态修补。在修复阶段,由于应用已经安装了集成有运行时SCA能力的IAST或RASP 探针,则可以一键下发或自定义漏洞利用触发告警或者漏洞利用阻断的热修复补丁,在不重启应用的情况下对漏洞调用路径进行监控和阻断。对于已经备案且暂时无需修复的漏洞,也可通过下发相关风险入口函策略,起到风险预警和监控的作用。
运行时SCA凭借精准识别数字应用运行加载时真正使用到的第三方组件及依赖,在应用测试和上线运营场景有着更广泛的应用。源鉴SCA在满足实现运行时SCA技术的基础上,结合二进制SCA技术、源码级检测技术及漏洞可达性分析等技术,有效帮助开发人员更好地管理和维护软件成分,减少无效漏洞的运营干扰,提高软件的安全性和可靠性,助力企业建立并有效落地数字供应链安全治理体系,保障数字供应链安全。