黑客欲用 coa 包挂马攻击,不料 npm 发包规范没学好......
封面图:iliched @ www.unsplash.com 。
1、周下载量 700w+ 的 npm 包 coa 昨晚(2021.11.4)让大片前端社区挂了,比如我们受影响的依赖链是 老框架 > @svgr/webpack > \@svgr/plugin-svgo > svgo@1.3.2 > coa
。应该是被黑客偷了发布权限,针对不同的 major 和 mirror 分别发布带问题的版本,其新增版本在 preinstall hook 里执行 start /B node compile.js & node compile.js
2、目的是挂马,但所幸遇到的是笨黑客,挂马没成功。安装依赖时直接报了 Error: Cannot find module 'path/to/compile.js'
的错误,惊动了整个前端社区,没成功的原因是发布时忘记在 files 里加上 compile.bat
和 compat.js
,看到下图,我直接笑出了声
3、大家可能都有这样的经历。1)本来项目跑地好好的,重装依赖后就挂了 2)跑了多年的老项目,新建的迭代突然就不能跑了,甚至工具 owner 早就离职了 3)睡一晚起来,发现 ESLint 规则变了,CI 跑不过了。这里有工具本身升级的问题,同时更大可能的则是三方依赖或其依赖更新引起的问题,还记得之前的 leftpad 事件吗?还记得 babel 两天更 8 个 bugfix 版本吗?
4、为什么有这个问题?抛开攻击导致的安全问题,还有 semver 理想和现实的差距。理想的 semver,是完全遵循 major break-change,mirror feature,bugfix bugfix 的世界,而显示的 semver,则是 major break-change,mirror break-change,bugfix break-change。造成的影响可大可小,构建报错、CI 报错、白屏、线上故障都可能发生
5、是个问题就有解,社区已经有了不少方案。比如 cnpm 的 bug-versions 可以忽略问题库,各个包管理都有提供 lock 功能来锁定整体依赖,package.json 中的 resolutions 配置可以锁定局部依赖,patch-package 库可以做代码级的临时修复
6、此外还有个解法是框架层直接锁依赖。业务代码的依赖链通常是 业务代码 > 框架 > 框架依赖 > 间接依赖
,在框架层锁定所有依赖是比较合适的,并且这个锁不止锁直接依赖,也要锁间接依赖。而 npm 并没有提供锁的方案,所以目前社区流行的方案是把依赖打包来达到锁间接依赖的目的
7、打包分两种。小型工具可以把自己合依赖一起打包,比如 rollup、rome、vite 等;大型框架通常需要拆包,并且有 node 侧和 browser 侧的依赖需要分开处理,通常会采用打包依赖的方式,比如把 node_modules/foo 编译到 compiled/foo,比如 next.js、umi
8、打包后你会发现依赖其实没想象中那么大。比如 webpack 打包后 3M、babel 全家桶打包后 3M、lodash 打包后 93K、axios 打包后 31K
9、打包依赖最大的好处是让框架和业务项目的安全稳定,比如 coa 的这次问题,对于 umi 3 以及内部依赖 umi 3 的业务框架和业务代码都没有任何影响。此外还有安装时 0 peerDependencies 警告、安装依赖时文件尺寸和数量下降,安装速度等提升
10、同事的总结是:0 error 0 warning 0 hijacking,更安全和稳定,依赖全锁定,再也不用担心 Babel 深夜发版,工具十年后依旧可用