|
打开依赖清单的此解那一刻,你看到的决方不是安全问题,是案包一张地图。上面标着所有别人替你写好的含具代码,以及它们各自带进来的有漏东西。有些东西你认识,此解有些你从没听说过,决方但它们都在你的案包项目里,跑在你的含具服务器上。 上周帮一个朋友看他的有漏Node项目。`npm audit`跑出来17个漏洞,此解3个critical。决方他盯着屏幕说:“这些包我都没直接用过。案包”对,含具问题就在这。有漏你装了Express,Express依赖cookie-parser,cookie-parser又依赖某个解析库,那个库三年前就没人维护了,但你的锁文件里还锁着它。攻击面从来不是你自己写的代码,是你间接引入的那几百个包。 有个案例我印象很深。`event-stream`这个包,每周下载量几百万次,某天突然被人注入了恶意代码,目标直指Copay钱包的私钥。维护者说自己把发布权限给了一个陌生人,因为对方“看起来很热心”。整个链条上没人做代码审查,因为大家都觉得“这是个基础包,应该没问题”。 这就是依赖链的真相:你信任的不是包本身,是包背后那个你可能永远联系不上的人。他可能是个学生,可能已经转行,可能某天账号被盗。而你,把生产环境的钥匙交给了他。 更麻烦的是传递性依赖。你直接依赖的包,作者可能很靠谱,但他依赖的那个包呢?他依赖的包依赖的包呢?npm的嵌套结构意味着一个漏洞可以藏在第五层,而你的安全扫描工具未必能穿透那么深。有些团队用`npm ls`看依赖树,输出长到需要重定向到文件才能看完。这不是夸张,React项目的依赖树轻松超过一千个节点。 修复这件事本身就是个坑。你升级了有漏洞的包,结果它的新版本改了API,你的代码得跟着改。或者更糟,新版本引入了另一个漏洞。有时候你发现漏洞只在某个特定配置下触发,而你恰好没用那个配置,但审计报告不会告诉你这些,它只标红。于是团队陷入两难:花两天升级一个根本不构成实际威胁的包,还是忽略它然后被合规部门追着跑? 我见过有人用`overrides`字段强制指定某个传递依赖的版本。这招能应急,但下次`npm install`可能又给你装回旧版。还有人干脆把整个`node_modules`提交到git,觉得这样至少能控制住内容。治标不治本,但能理解那种无力感。 说个具体的。`lodash`在4.17.11之前的版本有个原型污染漏洞,影响范围大到几乎所有Node项目。修复方式就是升级到4.17.12。听起来简单对吧?但当时很多项目锁定了旧版,因为新版改了一些边缘行为,测试挂了。于是有人选择不升,赌自己的代码没用到那个漏洞路径。这种赌博每天都在发生。 依赖管理工具在改进。`npm audit fix`能自动修一部分,Dependabot会提PR,Snyk能集成到CI里。但工具解决不了根本矛盾:你不可能审查每一行引入的代码。现代开发的效率建立在信任之上,而信任本身就是漏洞。 所以看到“此解决方案包含具有漏洞的包”这个提示时,别急着点“忽略”。花十分钟看看那个包是谁在维护,漏洞触发条件是什么,你的代码有没有走到那条路。大部分时候可能没事,但那个“大部分”里藏着所有事故。 |