什么是基于可达性的依赖分析?

来源:Endor Lab

作为软件工程师,我们(应该)努力写尽可能少的代码。这是可以理解的:新代码有新bug,以后需要维护。避免编写软件的一种方法是重用开源软件 (OSS) 库,这些库托管在集中式代码存储库中,例如 Maven 或 NPM。

工程挑战

依赖性声明规范的便利性导致了包管理器和包存储库的大量采用。虽然项目的启动成本似乎很低,但依赖重用并不是免费的。软件工程学术界已经详细记录了几个有关依赖重用的问题。从库用户的角度来看,很难跟踪依赖更新,尤其是对于传递依赖,并评估它们对客户端代码库的影响;库维护者在实践中很少遵守语义版本控制API 规定;将有价值的数据委托给包管理器自动下载的代码通常不是一个明智的选择。从库维护者的角度来看,很难在不破坏客户端的情况下改进 API,例如通过删除方法,同时缺乏对库代码进行专业维护的激励措施。此外,由于图书馆也可能依赖于其他图书馆,图书馆维护者面临着与图书馆用户同样的问题。

除了开发挑战之外,依赖性还会带来运营和安全方面的风险。长期以来,软件行业一直使用工具来管理运营风险的某些方面,例如许可证风险。但随着组织意识到对第三方软件的依赖程度,对运营风险的理解也在不断发展。如今,组织开始担心某个特定的第三方软件可能会被其维护者遗弃(通常称为“总线因素”),或者成为政治声明的目标。 

安全

当然,第三方代码中可能潜伏着漏洞,存在安全风险。同样,一段时间以来,软件行业一直在使用工具来跟踪这种风险,但近年来的两个分水岭事件给这项任务增添了全新的紧迫性。首先,solarwinds 的妥协让每个人都意识到了“软件供应链”的概念,以及链条早期阶段之一的妥协如何对位于供应末端的最终用户造成灾难性的且非常难以防止的故障链和毁灭性的“乘数”效应 然后,Log4J 妥协造成了一个噩梦般的场景,即一个漏洞很容易被远程利用,并且存在于 Java 生态系统中最流行的开源包之一中。这立即使大量软件和系统易受攻击,并导致非常大的补救成本,同时暴露了工具和组织响应手册的局限性(我是否使用 Log4J?在哪个软件中?有什么影响?)。所有这些甚至导致政府既试图帮助企业应对危险,又促使他们采取行动。

因此,今天软件的安全格局已经发生了变化。已经开发出针对包管理器以及发现和使用开源软件的整个基础架构的新型攻击。开源软件的消费者开始担心他们使用的开源软件的安全最佳实践以及该软件被破坏的可能性。努力开始识别、审计和强化“关键”开源包 [参考:谷歌的项目]。所有这些都是新的用例,现有工具几乎没有覆盖。与此同时,旧的漏洞管理工具(以及使用它们的组织)在不断加速发现的新漏洞流的重压下正在崩溃。简单地寻找针对开源软件发布的 CVE 逐渐变得无用,因为任何具有多个开源依赖项的中型项目几乎肯定在其依赖关系图中存在一些漏洞。即使这些漏洞可能根本不会被触发,要么是因为代码中没有通向它们的路径,要么是易受攻击软件的特定配置使得无法到达漏洞,安全团队被迫调查并采取 100s潜在的漏洞,因为他们缺乏获取适当上下文的工具。 

改变抽象

在 Endor 实验室,我们认为当前用于跟踪依赖项的工具(SBOM 生成器、更新通知器等)在错误的抽象级别工作:当开发人员发布和跟踪包时,实际重用发生在代码级别. 这种脱节是上述大多数问题的根源。如果我们能够以某种方式跟踪客户端程序正在重用的依赖项中代码的确切部分,我们将能够判断依赖项代码中的安全问题是否会影响客户端,依赖项更新是否可以安全执行,客户端是否链接到不兼容的许可代码以及代码删除是否会影响下游客户端。

因此,我们使用(静态)调用图作为分析依赖管理的更精确的工具。

什么是调用图?

调用图是应用程序分析以确定函数 f 是否调用另一个函数 y的结果。调用图可以是静态的也可以是动态的。

静态调用图

静态调用图的构造如下:我们通过使用 V 个唯一节点填充一个图来实例化分析,这些节点对应于程序中的函数及其依赖项。对于每个函数,我们检查函数体 (f) 并确定调用外部函数的所有位置(调用点)。然后,我们需要在集合 V 中识别调用 (y) 的目标。根据所使用的语言及其调用语义,我们可能需要为已识别的目标创建一条或多条边 (E)。这个过程一直持续到我们覆盖了所有呼叫站点。结果是图G=<V,E>,其中 E 中的边静态标识潜在的函数调用。

为什么是潜在的而不是精确的?由于调用语义。在具有静态分派且没有函数指针的语言中,我们确实可以创建完整的调用图。但实际上不存在这样有用的语言,因为抽象机制需要动态性。虚拟分派,即面向对象语言用于在运行时确定接口调用目标的机制,需要存在潜在调用目标的动态可更新表(称为 vtable)。为了使静态分析起作用,我们需要模仿 vtable 行为,因此我们需要链接所有潜在的调用。

动态调用图

如果可以检测程序的(语言或操作系统)运行时环境,动态调用图的构建就更直接了。如果是这种情况,我们可以注入探针(触发器),它会在 i) 每次进行函数调用时触发,以及 ii) 每次我们从函数调用返回时触发。然后我们需要为每个正在运行的线程维护一个堆栈:一旦函数调用完成,我们将被调用函数的名称压入堆栈;当函数返回时,我们弹出堆栈并将一对调用者 -> 被调用者写入输出。在执行结束时,写入的对自然形成一个图,这是特定执行的调用图。DTrace、callgrind 和各种分析器等工具使开发人员能够以直接的方式创建动态调用图。

权衡

静态和动态调用图之间存在各种权衡。由于上述原因,静态调用图是对程序实际行为的过度近似,因此不精确。这意味着,如果它们是天真地构建的,它们势必会给开发人员带来误报(例如,调用具有漏洞的依赖代码,而这在实践中可能永远不会发生)。有多种技术可以使静态调用图更加精确。另一方面,动态调用图实际上是精确的,因为它们代表了真实的程序执行;然而,它们并不完整,因为它们是一个(而非所有)可能的程序执行的结果。

在 Endor Labs,我们依靠静态调用图在细粒度级别执行依赖性分析。我们选择静态分析作为我们的武器,因为它对开发人员工作流程的干扰最小,可以在开发程序的同时向开发人员提供结果,不依赖于良好的测试覆盖率来提供结果,最重要的是,因为完整性是绝对的必须用于任何下游安全分析任务。我们世界一流的静态分析专家团队对每种编程语言应用最先进的静态分析,以最大限度地减少误报,达到当前技术水平所支持的程度。我们还在内部使用动态调用图来形成基准,我们的调用图将根据这些基准进行测量和优化。

为了处理生态系统规模,我们为每个包版本创建一个调用图,存储这些调用图,然后,给定一个客户端依赖集,我们即时拼接它们。在即将发布的帖子中,我们将通过展示我们的 Java 分析堆栈,详细说明我们如何能够将静态分析扩展到生态系统。

可达性和影响分析

一旦我们有了一个调用图,一个全新的清晰度水平就可以用于依赖分析。

调用图上下文信息的可用性对于解决安全和操作风险中的多个用例非常有价值。假设我们已经构建了我们软件的依赖关系图,知道它背后的调用图可以让我们确定两个关键信息:

  1. 可达性:它回答了“我的代码是否调用了一个易受攻击的函数?”这个问题。
  2. 影响:它回答了“如果我更改此功能,代码库的哪些部分会受到影响”的问题? 

它们听起来可能很相似,但它们的计算方式不同:

  1. 可达性涉及从客户端开始的调用图中的“前向”传递,并检查依赖集中的哪些包可以在调用图之后到达。如果没有通向包的路径,则该包不可访问。 
  2. 影响涉及调用图中的“向后”传递,从目标函数开始并返回调用图以确定调用此函数的可能位置。如果这些路径都没有通向客户端代码,则目标函数的影响集为空。

不难看出此上下文有多大用处。在依赖级别,如果我们知道无法从客户端应用程序访问依赖项,我们可以尝试将其删除,从而完全消除来自该依赖项的任何操作和安全风险。如果我们正在调查漏洞,并且我们知道涉及哪些易受攻击的功能,我们可以确定这些漏洞中的哪些是无法访问的,并且可以相应地优先考虑依赖项更新。其他更微妙的用例也成为可能。调用图信息将为我们提供有关使用“多少”依赖项的信息:我们是否仅使用此依赖项提供的多个 API 中的一个 API?我们是否可以替换或删除与我们的代码没有紧密耦合的依赖项,特别是如果它带来了很多潜在的风险?举个轶事,在我们自己的代码库中,有一个依赖项引入了大量其他依赖项,只是因为我们使用了一个非常简单的 API。如果没有这些工具,甚至不可能弄清楚这种情况正在发生,更不用说考虑如何解决它了。 

将上述内容与来自组合依赖关系和调用图的可见性相结合,我们正在构建一个非常强大的工具,允许开发人员或安全工程师展示如何在他们的应用程序中使用依赖关系,从哪里导入依赖关系,确定操作和每个依赖项带来的安全风险,并评估什么是补救此风险的最佳策略。

需要注意的是:计算调用图是复杂的并且本质上是不精确的,因此总是存在误报(我们认为某些东西可以到达但实际上不可达)和漏报(可以到达的东西被认为无法到达)的风险。不同的用例对误报和漏报的敏感度不同;例如,对于安全风险,漏报可能会产生问题。但最终,正确的工作流程将使这些工具的使用变得最有效:应该使用可达性信息来确定注意力和资源的优先级。安全团队的预算已经有限,无法处理每一个漏洞或审查每一个新的依赖项。通过提供正确的上下文,我们可以帮助团队最有效地确定优先级和使用有限的资源。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值