如何通过语言提供的能力来防范Log4j之类的漏洞?


功能能力安全(capability-safe)的语言(如Rust)可以最大限度地减少甚至防止Log4j漏洞发生。
在本文中讨论围绕Log4j漏洞的两个问题:

  1. 它会对用户提供的字符串进行字符串插值。
  2. 它会访问网络,而没有人意识到它可能会这样做。(这是能力安全的语言可以提供帮助的部分。)

。。。
log4j维护人员考虑实施引入导致漏洞的 JNDI 功能时,接下来可能会发生一些事情:
  1. 他们认为日志库在默认情况下始终访问网络是完全合理的,并Network 为该Logger.getLogger()方法添加了一个参数。一些用户接受了这一点并成为该漏洞的牺牲品,但更挑剔的用户担心此网络访问请求并切换到不需要它的更简单的其他日志记录库,从而避免了该漏洞。
  2. 他们认为日志库根本不应该访问网络,或者认为需要一个Network参数会让用户感到害怕或不方便,因此拒绝了该功能。
  3. 他们认为该功能能力是值得的,但不希望修改的破坏性 API 更改getLogger需要Network. 因此,他们引入了一种新方法,可能称为 Logger.getLoggerWithNetwork(MyClass.class, network). 由于大多数用户不使用这种方法并且log4j没有它就无法访问网络,因此可以防止绝大多数漏洞。

这三种可能性都比实际发生的情况要好,实际情况是:log4j突然获得了访问网络的能力,但其 API 并未更改以反映这一点,因此用户没有注意到
因此,功能安全的语言本可以节省或至少减轻这一点。
所以未来的语言设计师,请考虑让你的语言功能能力安全。
 
Rust基于能力的安全
操作系统有一个资源句柄或文件描述符的概念,它们是可以在程序内部和有时在程序之间传递的值,代表对外部资源的访问。程序通常具有 环境权限,只需提供其名称或地址即可请求任何文件或网络句柄:
let file = File::open("/anything/you/want.txt")?;

可能有访问控制列表、命名空间、防火墙或虚拟化机制来管理哪些资源可以实际访问,但这些通常是粗粒度的,并在应用程序之外进行配置。
基于能力的安全寻求避免环境权威,使沙箱更细粒度和可组合。要打开文件,需要一个Dir,代表它所在的打开目录:

let file = dir.open("the/thing.txt")?;

尝试访问未包含在目录中的路径:
let hidden = dir.open("../hidden.txt")?;

dir.symlink("/hidden.txt", "misdirection.txt")?;
let secret = dir.open("misdirection.txt")?;

返回PermissionDenied错误。
这允许应用程序逻辑配置自己的访问,而无需更改整个主机进程的行为、设置单独的主机进程或需要外部配置。
 
cap-std是Rust 标准库的基于能力的版本,cap-std项目围绕同名的cap-stdcrate 进行组织,并开发了一些库以简化基于功能能力的代码的编写,包括:

Cap-std 具有针对CWE-22 的保护功能,即“对受限目录的路径名的不当限制(‘路径遍历’)”,它在2021 年 CWE 前 25 名最危险的软件弱点中排名第 8 。它还可用于防止不受信任的输入诱导程序在 Linux 上打开“/proc/self/mem”。